Quel est le meilleur moyen de retourner XML à partir d'une action d'un contrôleur dans ASP.NET MVC? Il existe un bon moyen de retourner JSON, mais pas pour XML. Dois-je vraiment acheminer le XML par le biais d'une vue ou dois-je utiliser le moyen non recommandé de Response.L'écrire?
Utilisez l'action XmlResult de MVCContrib .
Pour référence voici leur code:
public class XmlResult : ActionResult { private object objectToSerialize; /// <summary> /// Initializes a new instance of the <see cref="XmlResult"/> class. /// </summary> /// <param name="objectToSerialize">The object to serialize to XML.</param> public XmlResult(object objectToSerialize) { this.objectToSerialize = objectToSerialize; } /// <summary> /// Gets the object to be serialized to XML. /// </summary> public object ObjectToSerialize { get { return this.objectToSerialize; } } /// <summary> /// Serialises the object that was passed into the constructor to XML and writes the corresponding XML to the result stream. /// </summary> /// <param name="context">The controller context for the current request.</param> 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");
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 XAttribute("version", "2.0"),
new XElement("child", "Hello World!")));
return new XmlActionResult(xml);
}
Cette ActionResult
personnalisée et réutilisable sérialise le XML pour vous.
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);
}
}
Vous pouvez spécifier un type MIME (tel que application/rss+xml
) et si la sortie doit être indentée si vous en avez besoin. Les deux propriétés ont des valeurs par défaut sensibles.
Si vous avez besoin d'un encodage autre que UTF8, il est simple d'ajouter une propriété pour cela aussi.
Si vous êtes seulement intéressé à renvoyer XML par une requête et que vous avez votre "chunk" xml, vous pouvez simplement faire (en tant qu'action dans votre contrôleur):
public string Xml()
{
Response.ContentType = "text/xml";
return yourXmlChunk;
}
Il y a un XmlResult (et bien plus) dans MVC Contrib. Jetez un oeil à http://www.codeplex.com/MVCContrib
Cela a récemment été le cas pour un projet Sitecore qui utilise une méthode pour créer un document XmlDocument à partir d'un élément Sitecore et de ses enfants et le renvoie à partir du contrôleur ActionResult en tant que fichier. Ma solution:
public virtual ActionResult ReturnXml()
{
return File(Encoding.UTF8.GetBytes(GenerateXmlFeed().OuterXml), "text/xml");
}
Enfin réussir à obtenir ce travail et pensé que je voudrais documenter comment ici dans l’espoir de sauver les autres la douleur.
Environnement
Navigateurs Web pris en charge
Ma tâche consistait à cliquer sur un bouton de l'interface utilisateur, appeler une méthode sur mon contrôleur (avec certains paramètres), puis lui renvoyer un fichier XML MS-Excel via une transformation xslt. Le XML MS-Excel renvoyé obligerait alors le navigateur à afficher la boîte de dialogue Ouvrir/Enregistrer. Cela devait fonctionner dans tous les navigateurs (listés ci-dessus).
Au début, j'ai essayé avec Ajax et de créer une ancre dynamique avec l'attribut "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. Des problèmes ont également été rencontrés pour essayer de déclencher par programme l'événement Click de l'ancre afin de provoquer le "téléchargement" réel.
Ce que j'ai fini par faire, c'est d'utiliser un IFRAME "invisible" et cela a fonctionné pour les 5 navigateurs!
Voici donc ce que j’ai trouvé: [veuillez noter que je ne suis en aucun cas un gourou html/javascript et que je n’ai inclus que le code correspondant]
HTML (extrait de bits pertinents)
<div id="docxOutput">
<iframe id="ifOffice" name="ifOffice" width="0" height="0"
hidden="hidden" seamless='seamless' frameBorder="0" scrolling="no"></iframe></div>
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 (extrait de code) @Drew a créé un ActionResult personnalisé appelé XmlActionResult, que j'ai modifié à cette fin.
Retourne le XML depuis l'action d'un contrôleur sous la forme d'un ActionResult?
Méthode My Controller (retourne ActionResult)
crée une instance de XmlActionResult modifié et la renvoie
XmlActionResult resultat = new XmlActionResult (excelXML, "application/vnd.ms-Excel"); chaîne version = DateTime.Now.ToString ("dd_MMM_yyyy_hhmmsstt"); string fileMask = "LabelExport_ {0} .xml";
result.DownloadFilename = string.Format (masque de fichier, version); retourne le résultat;
La principale modification apportée à la classe XmlActionResult créée par @Drew.
public override void ExecuteResult(ControllerContext context)
{
string lastModDate = DateTime.Now.ToString("R");
//Content-Disposition: attachment; filename="<file name.xml>"
// 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);
}
C'était fondamentalement ça. J'espère que ça aide les autres.
Une option simple qui vous permettra d'utiliser des flux et tout ce qui est return File(stream, "text/xml");
.
Voici un moyen simple de le faire:
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");
Une petite variante de réponse de Drew Noakes qui utilise la méthode Save () de 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)
}
}