Renvoyer le code XML d’une action du contrôleur en tant que ActionResult?

Quel est le meilleur moyen de renvoyer XML à partir de l’action d’un contrôleur dans ASP.NET MVC? Il existe un bon moyen de renvoyer JSON, mais pas pour XML. Dois-je vraiment acheminer le code XML via une vue ou dois-je procéder de la manière la moins pratique de Response.Writful?

Utilisez l’action XmlResult de MVCConsortingb.

Pour référence, voici leur 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(xmlSsortingng, "text/xml"); 

Si vous construisez le XML en utilisant l’excellent framework Linq-to-XML, cette approche sera utile.

Je crée un XDocument dans la méthode d’action.

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

Cette ActionResult personnalisée ActionResult sérialise le XML pour vous.

 public sealed class XmlActionResult : ActionResult { private readonly XDocument _document; public Formatting Formatting { get; set; } public ssortingng 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); } } 

Vous pouvez spécifier un type MIME (tel que application/rss+xml ) et indiquer si la sortie doit être mise en retrait si nécessaire. Les deux propriétés ont des valeurs par défaut sensibles.

Si vous avez besoin d’un encodage autre que UTF8, il est alors simple d’append une propriété pour cela.

Si vous souhaitez seulement renvoyer xml via une requête, et que vous avez votre “chunk” XML, vous pouvez simplement faire (en tant qu’action dans votre contrôleur):

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

Il y a un XmlResult (et beaucoup plus) dans MVC Consortingb. Jetez un coup d’oeil à http://www.codeplex.com/MVCConsortingb

J’ai dû le faire récemment pour un projet Sitecore qui utilise une méthode pour créer un XmlDocument à partir d’un élément Sitecore et de ses enfants et le renvoie depuis le contrôleur ActionResult en tant que fichier. Ma solution:

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

Enfin, réussissez à obtenir ce travail et pensais que je documenterais comment, dans l’espoir de sauver les autres.

Environnement

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

Navigateurs Web pris en charge

  • FireFox 23
  • IE 10
  • Chrome 29
  • Opera 16
  • Safari 5.1.7 (dernier pour Windows?)

Ma tâche consistait à cliquer sur un bouton d’interface utilisateur, à appeler une méthode sur mon contrôleur (avec certains parameters), puis à renvoyer un fichier XML XML via une transformation xslt. Le fichier MS-Excel XML renvoyé ferait alors apparaître le navigateur dans la boîte de dialog Ouvrir / Enregistrer. Cela devait fonctionner dans tous les navigateurs (listés ci-dessus).

Au début, j’ai essayé avec Ajax et créer une ancre dynamic avec l’atsortingbut “download” pour le nom de fichier, mais cela ne fonctionnait que pour environ 3 des 5 navigateurs (FF, Chrome, Opera) et non pour IE ou Safari. Et il y avait des problèmes avec essayer de déclencher par programmation l’événement Click de l’ancre pour provoquer le “téléchargement” réel.

Ce que j’ai fini par faire était d’utiliser un IFRAME “invisible” et cela fonctionnait pour les 5 navigateurs!

Voici donc ce que j’ai proposé: [veuillez noter que je ne suis en aucun cas un gourou html / javascript et que j’ai seulement inclus le code correspondant]

HTML (extrait de bits pertinents)

 

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 queryssortingng 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.setAtsortingbute("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 (extrait de code) @Drew a créé un ActionResult personnalisé appelé XmlActionResult que j’ai modifié dans mon but.

Renvoyer le code XML d’une action du contrôleur en tant que ActionResult?

Méthode My Controller (renvoie ActionResult)

  • transmet le paramètre keys à un serveur SQL Server stocké qui génère un fichier XML
  • ce XML est ensuite transformé via xslt dans un MS-Excel xml (XmlDocument)
  • crée l’instance de XmlActionResult modifiée et la retourne

    XmlActionResult result = new XmlActionResult (excelXML, “application / vnd.ms-excel”); version de chaîne = DateTime.Now.ToSsortingng (“dd_MMM_yyyy_hhmmsstt”); ssortingng fileMask = “LabelExport_ {0} .xml”;
    result.DownloadFilename = ssortingng.Format (fileMask, version); résultat de retour

La principale modification de la classe XmlActionResult créée par @Drew.

 public override void ExecuteResult(ControllerContext context) { ssortingng lastModDate = DateTime.Now.ToSsortingng("R"); //Content-Disposition: attachment; filename="" // must set the Content-Disposition so that the web browser will pop the open/save dialog ssortingng 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); } 

C’était fondamentalement ça. J’espère que ça aide les autres.

Une option simple qui vous permettra d’utiliser des stream et tout ce qui est de return File(stream, "text/xml"); .

Voici un moyen simple de le faire:

  var xml = new XDocument( new XElement("root", new XAtsortingbute("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"); 

Une petite variation de la réponse de Drew Noakes qui utilise la méthode Save () de XDocument.

 public sealed class XmlActionResult : ActionResult { private readonly XDocument _document; public ssortingng 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) } }