IT story

컨트롤러의 액션에서 XML을 ActionResult로 반환 하시겠습니까?

hot-time 2020. 6. 25. 07:53
반응형

컨트롤러의 액션에서 XML을 ActionResult로 반환 하시겠습니까?


ASP.NET MVC의 컨트롤러 작업에서 XML을 반환하는 가장 좋은 방법은 무엇입니까? JSON을 반환하는 좋은 방법이 있지만 XML은 아닙니다. 실제로 XML을 View를 통해 라우팅해야합니까? 아니면 최선의 방법으로 Response를 작성해야합니까?


MVCContrib 의 XmlResult 조치를 사용하십시오 .

여기에 그들의 코드가 있습니다 :

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

뛰어난 Linq-to-XML 프레임 워크를 사용하여 XML을 작성하는 경우이 방법이 도움이 될 것입니다.

XDocument액션 메소드를 작성합니다 .

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는 XML을 직렬화합니다.

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

MIME 유형 (예 application/rss+xml:)과 필요한 경우 출력을 들여 쓸지 여부를 지정할 수 있습니다 . 두 속성 모두 합리적인 기본값을 가지고 있습니다.

If you need an encoding other than UTF8, then it's simple to add a property for that too.


If you are only interested to return xml through a request, and you have your xml "chunk", you can just do (as an action in your controller):

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

There is a XmlResult (and much more) in MVC Contrib. Take a look at http://www.codeplex.com/MVCContrib


I've had to do this recently for a Sitecore project which uses a method to create an XmlDocument from a Sitecore Item and its children and returns it from the controller ActionResult as a File. My solution:

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

Finally manage to get this work and thought I would document how here in the hopes of saving others the pain.

Environment

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

Supported Web Browsers

  • FireFox 23
  • IE 10
  • Chrome 29
  • Opera 16
  • Safari 5.1.7 (last one for Windows?)

My task was on a ui button click, call a method on my Controller (with some params) and then have it return an MS-Excel XML via an xslt transform. The returned MS-Excel XML would then cause the browser to popup the Open/Save dialog. This had to work in all the browsers (listed above).

At first I tried with Ajax and to create a dynamic Anchor with the "download" attribute for the filename, but that only worked for about 3 of the 5 browsers(FF, Chrome, Opera) and not for IE or Safari. And there were issues with trying to programmatically fire the Click event of the anchor to cause the actual "download".

What I ended up doing was using an "invisible" IFRAME and it worked for all 5 browsers!

So here is what I came up with: [please note that I am by no means an html/javascript guru and have only included the relevant code]

HTML (snippet of relevant bits)

<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 (code snippet) @Drew created a custom ActionResult called XmlActionResult which I modified for my purpose.

Return XML from a controller's action in as an ActionResult?

My Controller method (returns ActionResult)

  • passes the keys parameter to a SQL Server stored proc that generates an XML
  • that XML is then transformed via xslt into an MS-Excel xml (XmlDocument)
  • creates instance of the modified XmlActionResult and returns it

    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, version); return result;

The main modification to the XmlActionResult class that @Drew created.

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

That was basically it. Hope it helps others.


A simple option that will let you use streams and all that is return File(stream, "text/xml");.


Here is a simple way of doing it:

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

A small variation of the answer from Drew Noakes that use the method Save() of 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)
    }
}

참고URL : https://stackoverflow.com/questions/134905/return-xml-from-a-controllers-action-in-as-an-actionresult

반응형