Вернуть XML из действия контроллера в виде ActionResult?

Как лучше всего вернуть XML из действия контроллера в ASP.NET MVC? Есть хороший способ вернуть JSON, но не для XML. Действительно ли мне нужно маршрутизировать XML через представление, или я должен использовать не самый лучший способ ответа.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
141
0
95 001
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

Ответ принят как подходящий

Используйте действие XmlResult MVCContrib.

Для справки вот их код:

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

Класс здесь взят прямо из проекта MVC Contrib. Не уверен, что это можно считать вашим собственным.

Sailing Judo 12.02.2009 18:37

Где бы вы поместили этот класс, если вы следуете соглашению ASP.NET MVC? Папка контроллеров? Возможно, там же, где вы бы разместили свои ViewModels?

p.campbell 03.09.2009 02:01

@pcampbel, я предпочитаю создавать отдельные папки в корне моего проекта для всех типов классов: результатов, фильтров, маршрутизации и т. д.

Anthony Serdyukov 06.04.2010 06:32

Использование XmlSerialiser и аннотаций членов может быть трудным в обслуживании. С тех пор, как Люк опубликовал этот ответ (около четырех лет назад), Linq to XML зарекомендовал себя как более элегантная и мощная замена для наиболее распространенных сценариев. Посмотрите на мой ответ пример того, как это сделать.

Drew Noakes 04.10.2012 12:35

В MVC Contrib есть XmlResult (и многое другое). Взгляните на http://www.codeplex.com/MVCContrib

Если вас интересует только возврат xml через запрос, и у вас есть «кусок» xml, вы можете просто сделать (как действие в вашем контроллере):

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

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

Вау, это мне действительно помогло, но тогда я только начинаю возиться с MVC.

Denis Valeev 27.05.2011 19:27

Если вы работаете с Linq to XML, создание строковой формы документа будет расточительным - это лучше работать с потоками.

Drew Noakes 04.10.2012 12:35

@ Дрю Ноукс: Нет, это не так. Если вы пишете напрямую в поток HttpContext.Response.Output, вы получите YSOD на серверах на базе WinXP. Кажется, это исправлено в Vista +, что особенно проблематично, если вы разрабатываете в Windows 7 и развертываете в Windows XP (Server 2003?). Если вы это сделаете, вам нужно сначала записать в поток памяти, а затем скопировать поток памяти в выходной поток ...

Stefan Steiger 17.04.2013 13:39

@Quandary, хорошо, я повторю мысль: создание строк расточительно, если вы можете избежать исключений выделения / сбора / нехватки памяти с помощью потоков, пока не вы работаете на вычислительных системах 11-летней давности, которые показывают ошибку.

Drew Noakes 17.04.2013 14:40

Вместо этого вы можете использовать mimetype application/xml.

Fred 02.09.2014 16:00

Можно ли с помощью этого установить кодировку UTF-8? Я попытался вернуть this.Content (xmlString, "text / xml", System.Text.Encoding.UTF8), но объявление XML всегда UTF-16

Andy B 13.03.2019 13:42

Если вы создаете XML, используя отличную структуру Linq-to-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, то для нее тоже просто добавить свойство.

Как вы думаете, можно ли изменить это для использования в контроллере API?

Ray Ackley 01.11.2012 05:34

@RayAckley, я не знаю, потому что я еще не пробовал новые возможности веб-API. Если вы узнаете, дайте нам знать.

Drew Noakes 03.11.2012 23:57

Я думаю, что ошибся с вопросом о контроллере API (обычно я не занимаюсь MVC). Я просто реализовал его как обычный контроллер, и он отлично работал.

Ray Ackley 23.11.2012 23:36

Отличная работа Дрю. Я использую ваш XmlActionResult для своих требований. Моя среда разработки: ASP.NET 4 MVC Я вызываю свой метод контроллера (возвращает XmlActionResult - содержащий преобразованный XML для MS-Excel) из ajax. Функция Ajax Success имеет параметр данных, содержащий преобразованный xml. Как использовать этот параметр данных для запуска окна браузера и отображения диалогового окна «Сохранить как» или просто открытия Excel?

sheir 19.09.2013 00:50

@sheir, если вы хотите, чтобы браузер запускал файл, вам не следует загружать его через AJAX. Просто перейдите прямо к своему методу действия. Тип MIME будет определять, как он обрабатывается браузером. Использование чего-то вроде application/octet-stream для принудительной загрузки. Я не знаю, какой тип MIME запускает Excel, но вы легко сможете найти его в Интернете.

Drew Noakes 19.09.2013 15:01

@DrewNoakes, только что опубликовал свое «решение» в ответе ниже. Еще раз спасибо за класс XmlActionResult.

sheir 26.09.2013 23:20

Текущий код излучает метку порядка байтов (BOM) в начале файла. Используйте new UTF8Encoding(false), чтобы удалить его.

ivarne 06.09.2019 10:22

@ivarne, вы также можете кэшировать экземпляр, чтобы уменьшить количество запросов.

Drew Noakes 08.09.2019 13:49

Да, но если вы используете Linq-to-xml, удаление единственного выделения, вероятно, будет несущественным

ivarne 09.09.2019 14:22

Наконец-то мне удалось получить эту работу, и я подумал, что запишу, как здесь, в надежде спасти других от боли.

Среда

  • VS2012
  • SQL Server 2008R2
  • .NET 4.5
  • ASP.NET MVC4 (бритва)
  • Windows 7

Поддерживаемые веб-браузеры

  • FireFox 23
  • IE 10
  • Хром 29
  • Опера 16
  • Safari 5.1.7 (последняя версия для Windows?)

Моя задача заключалась в нажатии кнопки пользовательского интерфейса, вызове метода на моем контроллере (с некоторыми параметрами), а затем возвращении MS-Excel XML через преобразование xslt. После этого возвращенный MS-Excel XML вызовет в браузере всплывающее диалоговое окно «Открыть / сохранить». Это должно было работать во всех браузерах (перечисленных выше).

Сначала я попытался использовать Ajax и создать динамический якорь с атрибутом «скачать» для имени файла, но это работало только примерно для 3 из 5 браузеров (FF, Chrome, Opera), а не для IE или Safari. И были проблемы с попыткой программно запустить событие Click привязки, чтобы вызвать фактическую «загрузку».

В итоге я использовал «невидимый» IFRAME, и он работал во всех 5 браузерах!

Итак, вот что я придумал: [обратите внимание, что я ни в коем случае не гуру html / javascript и включил только соответствующий код]

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# СЕРВЕРНАЯ СТОРОНА (фрагмент кода) @Drew создал настраиваемый ActionResult под названием XmlActionResult, который я изменил для своих целей.

Вернуть XML из действия контроллера в виде ActionResult?

Мой метод контроллера (возвращает ActionResult)

  • передает параметр ключей в хранимую процедуру SQL Server, которая генерирует XML
  • этот XML затем преобразуется через xslt в XML-файл MS-Excel (XmlDocument)
  • создает экземпляр измененного XmlActionResult и возвращает его

    Результат XmlActionResult = новый результат XmlActionResult (excelXML, «application / vnd.ms-excel»); строка version = DateTime.Now.ToString ("dd_MMM_yyyy_hhmmsstt"); строка fileMask = "LabelExport_ {0} .xml";
    result.DownloadFilename = string.Format (fileMask, версия); вернуть результат;

Основная модификация класса XmlActionResult, созданная @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);
}

Вот и все. Надеюсь, это поможет другим.

Простой вариант, который позволит вам использовать потоки и все такое - return File(stream, "text/xml");.

Вот простой способ сделать это:

        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 04.10.2018 22:58

Сделайте ms.Position=0, и вы сможете вернуть исходный поток памяти. Тогда можно return new FileStreamResult(ms,"text/xml");

Carter Medlin 28.01.2019 23:07

Недавно мне пришлось сделать это для проекта Sitecore, который использует метод для создания XmlDocument из элемента Sitecore и его дочерних элементов и возвращает его из контроллера ActionResult в виде файла. Мое решение:

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

Небольшая вариация ответ от Дрю Ноукс, использующая метод Save () 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)
    }
}

используйте один из этих методов

    public ContentResult GetXml()
    {
        string xmlString  = "your xml data";
        return Content(xmlString, "text/xml");
    }

или же

    public string GetXml()
    {
        string xmlString = "your xml data";
        Response.ContentType = "text/xml";
        return xmlString;
    }

Другие вопросы по теме