Gibt XML von einer Controller-Aktion als ActionResult zurück?

Was ist der beste Weg, XML von einer Controller-Aktion in ASP.NET MVC zurückzugeben? Es gibt eine schöne Möglichkeit, JSON zurückzugeben, aber nicht für XML. Muss ich das XML wirklich über eine Ansicht weiterleiten, oder sollte ich die nicht-bewährte Methode von Response verwenden?

Verwenden Sie MVCContribs XmlResult-Aktion.

Als Referenz hier ist ihr Code:

public class XmlResult : ActionResult { private object objectToSerialize; ///  /// Initializes a new instance of the  class. ///  /// The object to serialize to XML. public XmlResult(object objectToSerialize) { this.objectToSerialize = objectToSerialize; } ///  /// Gets the object to be serialized to XML. ///  public object ObjectToSerialize { get { return this.objectToSerialize; } } ///  /// Serialises the object that was passed into the constructor to XML and writes the corresponding XML to the result stream. ///  /// The controller context for the current request. public override void ExecuteResult(ControllerContext context) { if (this.objectToSerialize != null) { context.HttpContext.Response.Clear(); var xs = new System.Xml.Serialization.XmlSerializer(this.objectToSerialize.GetType()); context.HttpContext.Response.ContentType = "text/xml"; xs.Serialize(context.HttpContext.Response.Output, this.objectToSerialize); } } } 
 return this.Content(xmlString, "text/xml"); 

Wenn Sie das XML mithilfe des ausgezeichneten Linq-to-XML-Frameworks erstellen, ist dieser Ansatz hilfreich.

Ich erstelle ein XDocument in der Aktionsmethode.

 public ActionResult MyXmlAction() { // Create your own XDocument according to your requirements var xml = new XDocument( new XElement("root", new XAttribute("version", "2.0"), new XElement("child", "Hello World!"))); return new XmlActionResult(xml); } 

Dieses wiederverwendbare, benutzerdefinierte ActionResult serialisiert das XML für Sie.

 public sealed class XmlActionResult : ActionResult { private readonly XDocument _document; public Formatting Formatting { get; set; } public string MimeType { get; set; } public XmlActionResult(XDocument document) { if (document == null) throw new ArgumentNullException("document"); _document = document; // Default values MimeType = "text/xml"; Formatting = Formatting.None; } public override void ExecuteResult(ControllerContext context) { context.HttpContext.Response.Clear(); context.HttpContext.Response.ContentType = MimeType; using (var writer = new XmlTextWriter(context.HttpContext.Response.OutputStream, Encoding.UTF8) { Formatting = Formatting }) _document.WriteTo(writer); } } 

Sie können einen MIME-Typ angeben (z. B. application/rss+xml ) und ob die Ausgabe bei Bedarf eingerückt werden soll. Beide Eigenschaften haben vernünftige Standardwerte.

Wenn Sie eine andere Kodierung als UTF8 benötigen, können Sie auch einfach eine Eigenschaft hinzufügen.

Wenn Sie nur xml über eine Anfrage zurückgeben möchten und Sie Ihren xml “chunk” haben, können Sie das einfach tun (als Aktion in Ihrem Controller):

 public string Xml() { Response.ContentType = "text/xml"; return yourXmlChunk; } 

Es gibt ein XmlResult (und viel mehr) in MVC Contrib. Werfen Sie einen Blick auf http://www.codeplex.com/MVCContrib

Ich musste dies kürzlich für ein Sitecore-Projekt tun, das eine Methode zum Erstellen eines XmlDocuments aus einem Sitecore-Objekt und seinen untergeordneten Elementen verwendet und es vom Controller ActionResult als Datei zurückgibt. Meine Lösung:

 public virtual ActionResult ReturnXml() { return File(Encoding.UTF8.GetBytes(GenerateXmlFeed().OuterXml), "text/xml"); } 

Endlich gelang es mir, diese Arbeit zu bekommen und ich dachte, ich würde hier dokumentieren, in der Hoffnung, anderen den Schmerz zu ersparen.

Umgebung

  • VS2012
  • SQL Server 2008 R2
  • .NET 4.5
  • ASP.NET MVC4 (razor)
  • Windows 7

Unterstützte Webbrowser

  • FeuerFox 23
  • IE 10
  • Chrome 29
  • Oper 16
  • Safari 5.1.7 (letzter für Windows?)

Meine Aufgabe war es, mit einem Klick auf die Schaltfläche eine Methode auf meinem Controller (mit einigen Parametern) aufzurufen und dann über eine xslt-Transformation eine MS-Excel-XML zurückzugeben. Die zurückgegebene MS-Excel-XML würde dann dazu führen, dass der Browser das Öffnen / Speichern-Dialogfeld öffnet. Dies musste in allen oben genannten Browsern funktionieren.

Zuerst habe ich versucht, mit Ajax einen dynamischen Anchor mit dem “download” -Attribut für den Dateinamen zu erstellen, aber dieser funktionierte nur für etwa 3 der 5 Browser (FF, Chrome, Opera) und nicht für IE oder Safari. Und es gab Probleme mit dem Versuch, das Click-Ereignis des Ankers programmgesteuert auszulösen, um den eigentlichen “Download” zu verursachen.

Was ich getan habe, war ein “unsichtbares” IFRAME und es funktionierte für alle 5 Browser!

Also hier ist, was ich gefunden habe: [Bitte beachten Sie, dass ich auf keinen Fall ein HTML / Javascript Guru bin und nur den entsprechenden Code enthalten habe]

HTML (Ausschnitt relevanter Bits)

 

JAVASCRIPT

 //url to call in the controller to get MS-Excel xml var _lnkToControllerExcel = '@Url.Action("ExportToExcel", "Home")'; $("#btExportToExcel").on("click", function (event) { event.preventDefault(); $("#ProgressDialog").show();//like an ajax loader gif //grab the basket as xml var keys = GetMyKeys();//returns delimited list of keys (for selected items from UI) //potential problem - the querystring might be too long?? //2K in IE8 //4096 characters in ASP.Net //parameter key names must match signature of Controller method var qsParams = [ 'keys=' + keys, 'locale=' + '@locale' ].join('&'); //The element with id="ifOffice" var officeFrame = $("#ifOffice")[0]; //construct the url for the iframe var srcUrl = _lnkToControllerExcel + '?' + qsParams; try { if (officeFrame != null) { //Controller method can take up to 4 seconds to return officeFrame.setAttribute("src", srcUrl); } else { alert('ExportToExcel - failed to get reference to the office iframe!'); } } catch (ex) { var errMsg = "ExportToExcel Button Click Handler Error: "; HandleException(ex, errMsg); } finally { //Need a small 3 second ( delay for the generated MS-Excel XML to come down from server) setTimeout(function () { //after the timeout then hide the loader graphic $("#ProgressDialog").hide(); }, 3000); //clean up officeFrame = null; srcUrl = null; qsParams = null; keys = null; } }); 

C # SERVER-SIDE (Code-Snippet) @Drew hat ein benutzerdefiniertes ActionResult namens XmlActionResult erstellt, das ich für meinen Zweck modifiziert habe.

Gibt XML von einer Controller-Aktion als ActionResult zurück?

Meine Controller-Methode (gibt ActionResult zurück)

  • übergibt den Parameter keys an einen gespeicherten SQL-Serverproc, der ein XML generiert
  • Dieses XML wird dann über xslt in eine MS-Excel xml (XmlDocument) umgewandelt
  • erstellt Instanz des modifizierten XmlActionResult und gibt es zurück

    XmlActionResult Ergebnis = neues XmlActionResult (ExcelXML, “application / vnd.ms-excel”); Zeichenfolgenversion = DateTime.Now.ToString (“dd_MMM_yyyy_hhmmstt”); Zeichenfolge fileMask = “LabelExport_ {0} .xml”;
    result.DownloadFilename = string.Format (fileMask, Version); Ergebnis zurückgeben;

Die Hauptänderung der XmlActionResult-class, die @Drew erstellt hat.

 public override void ExecuteResult(ControllerContext context) { string lastModDate = DateTime.Now.ToString("R"); //Content-Disposition: attachment; filename="" // must set the Content-Disposition so that the web browser will pop the open/save dialog string disposition = "attachment; " + "filename=\"" + this.DownloadFilename + "\"; "; context.HttpContext.Response.Clear(); context.HttpContext.Response.ClearContent(); context.HttpContext.Response.ClearHeaders(); context.HttpContext.Response.Cookies.Clear(); context.HttpContext.Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);// Stop Caching in IE context.HttpContext.Response.Cache.SetNoStore();// Stop Caching in Firefox context.HttpContext.Response.Cache.SetMaxAge(TimeSpan.Zero); context.HttpContext.Response.CacheControl = "private"; context.HttpContext.Response.Cache.SetLastModified(DateTime.Now.ToUniversalTime()); context.HttpContext.Response.ContentType = this.MimeType; context.HttpContext.Response.Charset = System.Text.UTF8Encoding.UTF8.WebName; //context.HttpContext.Response.Headers.Add("name", "value"); context.HttpContext.Response.Headers.Add("Last-Modified", lastModDate); context.HttpContext.Response.Headers.Add("Pragma", "no-cache"); // HTTP 1.0. context.HttpContext.Response.Headers.Add("Expires", "0"); // Proxies. context.HttpContext.Response.AppendHeader("Content-Disposition", disposition); using (var writer = new XmlTextWriter(context.HttpContext.Response.OutputStream, this.Encoding) { Formatting = this.Formatting }) this.Document.WriteTo(writer); } 

Das war es im Grunde. Hoffe es hilft anderen.

Eine einfache Option, mit der Sie Streams verwenden können und alles, was return File(stream, "text/xml"); .

Hier ist eine einfache Art, es zu tun:

  var xml = new XDocument( new XElement("root", new XAttribute("version", "2.0"), new XElement("child", "Hello World!"))); MemoryStream ms = new MemoryStream(); xml.Save(ms); return File(new MemoryStream(ms.ToArray()), "text/xml", "HelloWorld.xml"); 

Eine kleine Variation der Antwort von Drew Noakes , die die Methode Save () von XDocument verwendet.

 public sealed class XmlActionResult : ActionResult { private readonly XDocument _document; public string MimeType { get; set; } public XmlActionResult(XDocument document) { if (document == null) throw new ArgumentNullException("document"); _document = document; // Default values MimeType = "text/xml"; } public override void ExecuteResult(ControllerContext context) { context.HttpContext.Response.Clear(); context.HttpContext.Response.ContentType = MimeType; _document.Save(context.HttpContext.Response.OutputStream) } }