XML को एक नियंत्रक के एक्शन से एक ActionResult के रूप में लौटाएं?


139

ASP.NET MVC में नियंत्रक की कार्रवाई से XML को वापस करने का सबसे अच्छा तरीका क्या है? JSON को वापस करने का एक अच्छा तरीका है, लेकिन XML के लिए नहीं। क्या मुझे वास्तव में एक दृश्य के माध्यम से XML को रूट करने की आवश्यकता है, या क्या मुझे Response.Write-ing का सबसे अच्छा अभ्यास तरीका नहीं करना चाहिए?

जवाबों:


114

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

12
यहां के वर्ग को एमवीसी कंट्रिब परियोजना से सीधे लिया जाता है। यकीन नहीं है कि अगर thats क्या अपने खुद के रोलिंग के रूप में योग्य है।
सेलिंग जूडो

3
यदि आप ASP.NET MVC सम्मेलन का अनुसरण कर रहे हैं, तो आप इस वर्ग को कहां रखेंगे? कंट्रोलर फोल्डर? एक ही जगह आप अपने ViewModels, शायद डाल दिया था?
p। कॉंपबेल

7
@ pcampbel, मैं हर तरह की कक्षाओं के लिए अपने प्रोजेक्ट रूट में अलग-अलग फ़ोल्डर बनाना पसंद करता हूं: परिणाम, फ़िल्टर, रूटिंग, आदि
एंथोनी सेरड्यूकोव

उपयोग XmlSerialiserऔर सदस्य एनोटेशन को बनाए रखना मुश्किल हो सकता है। चूंकि ल्यूक ने यह उत्तर (लगभग चार साल पहले) पोस्ट किया था, लिनेक ने एक्सएमएल को खुद को सबसे आम परिदृश्यों के लिए अधिक सुरुचिपूर्ण और शक्तिशाली प्रतिस्थापन के रूप में साबित किया है। यह कैसे करना है, इसके एक उदाहरण के लिए मेरा उत्तर देखें ।
ड्रू नोक

133
return this.Content(xmlString, "text/xml");

1
वाह, यह वास्तव में मेरी मदद करता है, लेकिन फिर मैं केवल एमवीसी चीज़ के साथ छेड़छाड़ करने लगा हूं।
डेनिस वलेव

यदि आप Linq से XML के साथ काम कर रहे हैं, तो दस्तावेज़ का एक स्ट्रिंग रूप बनाना बेकार है - धाराओं के साथ काम करना बेहतर है
ड्रू नोक

2
@ ड्रू नोक: नहीं यह नहीं है। यदि आप सीधे HttpContext.Response.Output स्ट्रीम पर लिखते हैं, तो आपको WinXP आधारित सर्वर पर YSOD मिलेगा। यह विस्टा + पर तय किया गया लगता है, जो विशेष रूप से समस्याग्रस्त है यदि आप विंडोज 7 पर विकसित होते हैं और विंडोज एक्सपी (सर्वर 2003) पर तैनात होते हैं। यदि आप करते हैं, तो आपको पहले एक मेमोरी स्ट्रीम पर लिखना होगा, और फिर मेमोरी स्ट्रीम को आउटपुट स्ट्रीम पर कॉपी करना होगा ...
स्टीफन स्टीगर

6
@ प्रश्न, ठीक है, मैं इस बिंदु को विश्राम दूंगा: तार बनाना तब व्यर्थ है जब आप धाराओं का उपयोग करके आवंटन / संग्रह / आउट-ऑफ-मेमोरी-अपवादों से बच सकते थे, जब तक कि आप 11 साल पुराने कंप्यूटिंग सिस्टम पर काम नहीं कर रहे हैं जो एक त्रुटि का प्रदर्शन करते हैं।
ड्रू नोक

1
आप application/xmlइसके बजाय mimetype का उपयोग करना चाह सकते हैं ।
फ्रेड

32

यदि आप उत्कृष्ट 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) निर्दिष्ट कर सकते हैं और यदि आवश्यक हो तो आउटपुट को इंडेंट किया जाना चाहिए। दोनों गुणों में समझदार चूक हैं।

यदि आपको UTF8 के अलावा किसी अन्य एन्कोडिंग की आवश्यकता है, तो उसके लिए भी एक संपत्ति जोड़ना आसान है।


क्या आपको लगता है कि एपीआई नियंत्रक में उपयोग के लिए इसे संशोधित करना संभव है?
रे एकली

@ रेकले, मुझे नहीं पता क्योंकि मैंने अभी तक नए वेब एपीआई सामान की कोशिश नहीं की है। अगर आपको पता चले तो हमें बताएं।
ड्रू नोक

मुझे लगता है कि मैं एपीआई नियंत्रक प्रश्न के साथ गलत रास्ते पर था (मैं सामान्य रूप से एमवीसी सामान नहीं करता हूं)। मैंने इसे नियमित नियंत्रक के रूप में लागू किया और इसने बहुत अच्छा काम किया।
रे एकली

महान काम आकर्षित किया। मैं अपनी आवश्यकता के लिए आपके XmlActionResult के स्वाद का उपयोग कर रहा हूं। मेरा देव वातावरण: ASP.NET 4 MVC मैं ajax से अपने नियंत्रक की विधि (XmlActionResult - MS-Excel के लिए एक परिवर्तित xml युक्त) देता हूं। अजाक्स सक्सेस फंक्शन में एक डेटा पैरामीटर होता है जिसमें ट्रांसफ़ॉर्मेड xml होता है। ब्राउज़र विंडो को लॉन्च करने के लिए इस डेटा पैरामीटर का उपयोग कैसे करें और या तो SaveAs डायलॉग प्रदर्शित करें या केवल Excel खोलें?
Sheir

@ शिशिर, यदि आप चाहते हैं कि ब्राउज़र फ़ाइल को लॉन्च करे तो आपको इसे AJAX के माध्यम से लोड नहीं करना चाहिए। बस अपनी कार्य विधि पर सीधे नेविगेट करें। MIME प्रकार यह निर्धारित करेगा कि यह ब्राउज़र द्वारा कैसे नियंत्रित किया जाता है। application/octet-streamइसे डाउनलोड करने के लिए मजबूर करने के लिए कुछ का उपयोग करना । मुझे नहीं पता कि MIME टाइप एक्सेल लॉन्च करता है, लेकिन आपको इसे आसानी से ऑनलाइन ढूंढने में सक्षम होना चाहिए।
ड्रू नोकें

26

यदि आप केवल एक अनुरोध के माध्यम से xml वापस करने के इच्छुक हैं, और आपके पास आपका xml "chunk" है, तो आप बस कर सकते हैं (अपने नियंत्रक में एक कार्रवाई के रूप में):

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


4

मुझे यह हाल ही में एक Sitecore प्रोजेक्ट के लिए करना है, जो एक Sitecore आइटम और उसके बच्चों से XmlDocument बनाने के लिए एक विधि का उपयोग करता है और इसे एक फ़ाइल के रूप में कंट्रोलर ActionResult से लौटाता है। मेरा समाधान:

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

2

अंत में इस काम को प्राप्त करने के लिए प्रबंधन करें और सोचा कि मैं यहां दस्तावेज दूंगा कि दूसरों को दर्द से बचाने की उम्मीद में यहां कैसे।

वातावरण

  • VS2012
  • SQL सर्वर 2008R2
  • .NET 4.5
  • ASP.NET MVC4 (रेज़र)
  • विंडोज 7

समर्थित वेब ब्राउज़र

  • फायरफोक्स 23
  • IE १०
  • क्रोम 29
  • ओपेरा 16
  • सफारी 5.1.7 (विंडोज के लिए अंतिम एक?)

मेरा कार्य ui बटन क्लिक पर था, मेरे नियंत्रक पर एक विधि को कॉल करें (कुछ params के साथ) और फिर इसे xslt परिवर्तन के माध्यम से MS-Excel XML लौटाएं। MS-Excel XML लौटाया गया तो ब्राउज़र ओपन / सेव डायलॉग को पॉपअप करेगा। यह सभी ब्राउज़रों (ऊपर सूचीबद्ध) में काम करना था।

पहले तो मैंने अजाक्स के साथ और फ़ाइलनाम के लिए "डाउनलोड" विशेषता के साथ एक गतिशील एंकर बनाने की कोशिश की, लेकिन यह केवल 5 ब्राउज़रों (एफएफ, क्रोम, ओपेरा) में से लगभग 3 के लिए काम किया और आईई या सफारी के लिए नहीं। और वास्तविक "डाउनलोड" का कारण बनने के लिए एंकर के क्लिक इवेंट को प्रोग्रामिक रूप से फायर करने की कोशिश करने के मुद्दे थे।

मैंने जो किया वह एक "अदृश्य" IFRAME का उपयोग करते हुए समाप्त हो गया और इसने सभी 5 ब्राउज़रों के लिए काम किया!

तो यहाँ है कि मैं क्या लेकर आया हूँ: [कृपया ध्यान दें कि मैं किसी भी तरह से html / जावास्क्रिप्ट गुरु नहीं हूँ और केवल प्रासंगिक कोड शामिल किया है]

HTML (प्रासंगिक बिट्स का स्निपेट)

<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 (कोड स्निपेट) @ ड्रू ने XmlActionResult नामक एक कस्टम ActionResult बनाया है जिसे मैंने अपने उद्देश्य के लिए संशोधित किया है।

XML को एक नियंत्रक के एक्शन से एक ActionResult के रूप में लौटाएं?

मेरा नियंत्रक तरीका (ActionResult लौटाता है)

  • SQL सर्वर संग्रहीत संग्रह के लिए कुंजी पैरामीटर पास करता है जो XML उत्पन्न करता है
  • तब XML को MS-Excel xml (XmlDocument) में xslt के माध्यम से बदल दिया जाता है
  • संशोधित XmlActionResult का उदाहरण बनाता है और इसे लौटाता है

    XmlActionResult परिणाम = नया XmlActionResult (excelXML, "एप्लिकेशन / vnd.ms-excel)"; स्ट्रिंग संस्करण = DateTime.Now.ToString ("dd_MMM_yyyy_hhmmsstb"); string fileMask = "LabelExport_ {0} .xml";
    result.DownloadFilename = string.Format (fileMask, संस्करण); वापसी परिणाम;

XDActionResult वर्ग का मुख्य संशोधन जो @ बनाया गया था।

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

वह मूल रूप से यह था। आशा है कि यह दूसरों की मदद करता है।


1

एक सरल विकल्प जो आपको धाराओं और सभी का उपयोग करने देगा return File(stream, "text/xml");


0

यहाँ यह करने का एक सरल तरीका है:

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

यह दो मेमोरी स्ट्रीम क्यों बनाता है? सिर्फ msएक नए की नकल करने के बजाय सीधे पास क्यों नहीं ? दोनों वस्तुओं का जीवनकाल समान होगा।
jpaugh

एक करें ms.Position=0और आप मूल मेमोरीस्ट्रीम वापस कर सकते हैं। फिर आप कर सकते हैंreturn new FileStreamResult(ms,"text/xml");
कार्टर मेडलिन

0

Drew Noakes के जवाब का एक छोटा सा बदलाव जो XDocument के Save () विधि का उपयोग करता है।

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)
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.