Restituisce XML dall’azione di un controller come ActionResult?

Qual è il modo migliore per restituire XML dall’azione di un controller in ASP.NET MVC? C’è un buon modo per restituire JSON, ma non per XML. Ho davvero bisogno di indirizzare l’XML attraverso una vista, o dovrei fare il modo migliore per reactjs. Scrivilo?

Utilizzare l’azione XmlResult di MVCContrib.

Per riferimento qui è il loro codice:

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"); 

Se stai costruendo l’XML utilizzando l’eccellente framework Linq-to-XML, allora questo approccio sarà utile.

Creo un XDocument nel metodo di azione.

 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); } 

ActionResult personalizzato e riutilizzabile serializza l’XML per te.

 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); } } 

È ansible specificare un tipo MIME (come application/rss+xml ) e se l’output dovrebbe essere rientrato se necessario. Entrambe le proprietà hanno valori predefiniti sensibili.

Se hai bisogno di una codifica diversa da UTF8, è semplice aggiungere una proprietà anche per questo.

Se sei interessato solo a restituire xml attraverso una richiesta, e hai il tuo xml “chunk”, puoi semplicemente fare (come azione nel tuo controller):

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

C’è un XmlResult (e molto altro) in MVC Contrib. Dai un’occhiata a http://www.codeplex.com/MVCContrib

Ho dovuto farlo di recente per un progetto Sitecore che utilizza un metodo per creare un XmlDocument da un object Sitecore e dai suoi figli e lo restituisce dal controller ActionResult come file. La mia soluzione:

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

Finalmente riesco a ottenere questo lavoro e ho pensato che avrei documentato come qui nella speranza di salvare il dolore agli altri.

Ambiente

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

Browser Web supportati

  • FireFox 23
  • IE 10
  • Chrome 29
  • Opera 16
  • Safari 5.1.7 (l’ultimo per Windows?)

Il mio compito era con un clic del pulsante dell’interfaccia utente, chiamare un metodo sul mio controller (con alcuni parametri) e poi restituire un XML MS-Excel tramite una trasformazione xslt. L’XML MS-Excel restituito indurrebbe quindi il browser a visualizzare la finestra di dialogo Apri / Salva. Questo ha dovuto funzionare in tutti i browser (elencati sopra).

All’inizio ho provato con Ajax e creare un ancoraggio dinamico con l’attributo “download” per il nome del file, ma funzionava solo per circa 3 dei 5 browser (FF, Chrome, Opera) e non per IE o Safari. E c’erano problemi con il tentativo di triggersre a livello di codice l’evento Click dell’ancora per causare l’effettivo “download”.

Quello che ho finito è stato usare un IFRAME “invisibile” e ha funzionato per tutti e 5 i browser!

Quindi ecco cosa mi è venuto in mente: [si noti che non sono affatto un guru html / javascript e ho incluso solo il codice pertinente]

HTML (snippet di bit rilevanti)

 

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 (snippet di codice) @Drew ha creato un ActionResult personalizzato chiamato XmlActionResult che ho modificato per il mio scopo.

Restituisce XML dall’azione di un controller come ActionResult?

Metodo My Controller (restituisce ActionResult)

  • passa il parametro keys a un proc memorizzato di SQL Server che genera un XML
  • che XML viene quindi trasformato tramite xslt in un MS-Excel xml (XmlDocument)
  • crea un’istanza del XmlActionResult modificato e lo restituisce

    XmlActionResult result = new XmlActionResult (excelXML, “application / vnd.ms-excel”); string version = DateTime.Now.ToString (“dd_MMM_yyyy_hhmmsstt”); string fileMask = “LabelExport_ {0} .xml”;
    result.DownloadFilename = string.Format (fileMask, versione); ritorno risultato;

La modifica principale della class XmlActionResult creata da @Drew.

 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); } 

Questo era fondamentalmente. Spero che aiuti gli altri.

Una semplice opzione che ti permetterà di usare i flussi e tutto ciò che è di return File(stream, "text/xml"); .

Ecco un modo semplice per farlo:

  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"); 

Una piccola variazione della risposta di Drew Noakes che utilizza il metodo Save () di XDocument.

 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) } }