Каков наилучший способ вернуть XML из действия контроллера в ASP.NET MVC? Есть хороший способ вернуть JSON, но не для XML. Должен ли я действительно направлять XML через View, или я должен сделать не-наилучшую практику Response.Write-it?
Используйте 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");
Если вы строите 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, тогда просто добавить свойство для этого.
Если вам только интересно вернуть xml через запрос, и у вас есть xml-chunk, вы можете просто сделать (как действие в вашем контроллере):
public string Xml()
{
Response.ContentType = "text/xml";
return yourXmlChunk;
}
В MVC Contrib. имеется XmlResult (и многое другое). Взгляните на http://www.codeplex.com/MVCContrib
Наконец-то удастся получить эту работу и подумать, что я буду документировать, как здесь, в надежде спасти других от боли.
Окружающая среда
Поддерживаемые веб-браузеры
Моя задача была нажата кнопкой мыши, вызовите метод на моем контроллере (с некоторыми параметрами), а затем верните XML-документ MS-Excel через преобразование xslt. Возвращенный MS-Excel XML приведет к тому, что браузер откроет диалоговое окно "Открыть/Сохранить". Это должно было работать во всех браузерах (см. Выше).
Сначала я попытался с Ajax и создать динамический Anchor с атрибутом "загрузка" для имени файла, но это работает только для 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;
}
});
С# SERVER-SIDE (фрагмент кода) @Drew создал пользовательский ActionResult под названием XmlActionResult, который я изменил для своих целей.
Вернуть XML из действия контроллера в ActionResult?
Мой контроллер (возвращает ActionResult)
создает экземпляр модифицированного XmlActionResult и возвращает его
Результат XmlActionResult = новый 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, версия);
результат возврата;
Основная модификация класса 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);
}
Это было в основном. Надеюсь, это поможет другим.
Мне нужно было сделать это недавно для проекта Sitecore, который использует метод для создания XmlDocument из элемента Sitecore и его дочерних элементов и возвращает его из контроллера ActionResult в качестве файла. Мое решение:
public virtual ActionResult ReturnXml()
{
return File(Encoding.UTF8.GetBytes(GenerateXmlFeed().OuterXml), "text/xml");
}
Простая опция, которая позволит вам использовать потоки и все, что есть return File(stream, "text/xml");
.
Небольшой вариант ответа от Drew Noakes, который использует метод 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)
}
}
Вот простой способ сделать это:
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");