Как лучше всего вернуть XML из действия контроллера в ASP.NET MVC? Есть хороший способ вернуть JSON, но не для XML. Действительно ли мне нужно направлять XML через представление, или я должен использовать не самый лучший способ ответа.
Как лучше всего вернуть XML из действия контроллера в ASP.NET MVC? Есть хороший способ вернуть JSON, но не для XML. Действительно ли мне нужно направлять XML через представление, или я должен использовать не самый лучший способ ответа.
Ответы:
Используйте действие 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);
}
}
}
XmlSerialiser
аннотаций и членов может быть сложно поддерживать. С тех пор, как Люк опубликовал этот ответ (около четырех лет назад), Linq to XML зарекомендовал себя как более элегантная и мощная замена для большинства распространенных сценариев. Посмотрите мой ответ, чтобы увидеть, как это сделать.
return this.Content(xmlString, "text/xml");
application/xml
Вместо этого вы можете использовать mimetype.
Если вы создаете 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, то для нее тоже просто добавить свойство.
application/octet-stream
принудительного скачивания. Я не знаю, какой тип MIME запускает Excel, но вы сможете достаточно легко найти его в Интернете.
В MVC Contrib есть XmlResult (и многое другое). Взгляните на http://www.codeplex.com/MVCContrib
Недавно мне пришлось сделать это для проекта Sitecore, который использует метод для создания XmlDocument из элемента Sitecore и его дочерних элементов и возвращает его из контроллера ActionResult в виде файла. Мое решение:
public virtual ActionResult ReturnXml()
{
return File(Encoding.UTF8.GetBytes(GenerateXmlFeed().OuterXml), "text/xml");
}
Наконец-то удалось получить эту работу, и я подумал, что запишу, как здесь, в надежде спасти других от боли.
Окружающая среда
Поддерживаемые веб-браузеры
Моя задача заключалась в нажатии кнопки пользовательского интерфейса, вызове метода на моем контроллере (с некоторыми параметрами), а затем в том, чтобы он возвращал XML MS-Excel через преобразование 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 # SERVER-SIDE (фрагмент кода) @Drew создал настраиваемый ActionResult под названием XmlActionResult, который я модифицировал для своих целей.
Вернуть XML из действия контроллера в виде ActionResult?
Мой метод контроллера (возвращает ActionResult)
создает экземпляр измененного 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);
}
Вот и все. Надеюсь, это поможет другим.
Вот простой способ сделать это:
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
напрямую, а не копировать на новый? Оба объекта будут иметь одинаковое время жизни.
ms.Position=0
и вы сможете вернуть исходный поток памяти. Тогда вы можетеreturn new FileStreamResult(ms,"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)
}
}