web-dev-qa-db-fra.com

Renvoyer du code XML à partir de l'action d'un contrôleur en tant que ActionResult?

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?

135
Ken Randall

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);
        }
    }
}
112
Luke Smith
return this.Content(xmlString, "text/xml");
127
Petr

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.

30
Drew Noakes

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;
}
24
Erik

Il y a un XmlResult (et bien plus) dans MVC Contrib. Jetez un oeil à http://www.codeplex.com/MVCContrib

17
Mahdi Taghizadeh

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");
}
4
Matthew Price

Enfin réussir à obtenir ce travail et pensé que je voudrais documenter comment ici dans l’espoir de sauver les autres la douleur.

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 (le dernier pour Windows?)

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)

  • passe le paramètre keys à un proc stocké SQL Server qui génère un XML
  • ce XML est ensuite transformé via xslt en un fichier MS-Excel xml (XmlDocument)
  • 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.

2
sheir

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

1
Casey

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

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