【发布时间】:2010-09-13 04:33:43
【问题描述】:
在 ASP.NET MVC 中从控制器的操作返回 XML 的最佳方式是什么?有一种返回 JSON 的好方法,但不适用于 XML。我真的需要通过 View 路由 XML,还是应该使用 Response.Write 的非最佳实践方式?
【问题讨论】:
标签: asp.net .net xml asp.net-mvc
在 ASP.NET MVC 中从控制器的操作返回 XML 的最佳方式是什么?有一种返回 JSON 的好方法,但不适用于 XML。我真的需要通过 View 路由 XML,还是应该使用 Response.Write 的非最佳实践方式?
【问题讨论】:
标签: asp.net .net xml asp.net-mvc
return this.Content(xmlString, "text/xml");
【讨论】:
application/xml mimetype。
使用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); } } }
【讨论】:
XmlSerialiser 和成员注释可能很难维护。自从 Luke 发布了这个答案(大约四年前)以来,Linq to XML 已经证明自己是大多数常见场景的更优雅、更强大的替代品。查看my answer 了解如何执行此操作的示例。
如果您使用出色的 Linq-to-XML 框架构建 XML,那么这种方法会很有帮助。
我在 action 方法中创建了一个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,但您应该可以很容易地在网上找到它。
如果你只对通过请求返回 xml 感兴趣,并且你有你的 xml“块”,你可以这样做(作为你的控制器中的一个动作):
public string Xml()
{
Response.ContentType = "text/xml";
return yourXmlChunk;
}
【讨论】:
在 MVC Contrib 中有一个 XmlResult(以及更多)。看看http://www.codeplex.com/MVCContrib
【讨论】:
我最近不得不为一个 Sitecore 项目执行此操作,该项目使用一种方法从 Sitecore 项及其子项创建 XmlDocument,并将其从控制器 ActionResult 作为文件返回。我的解决方案:
public virtual ActionResult ReturnXml()
{
return File(Encoding.UTF8.GetBytes(GenerateXmlFeed().OuterXml), "text/xml");
}
【讨论】:
终于设法完成了这项工作,并认为我会在这里记录下如何在这里为他人省去痛苦。
环境
支持的网络浏览器
我的任务是单击 ui 按钮,在我的控制器上调用一个方法(带有一些参数),然后让它通过 xslt 转换返回一个 MS-Excel XML。返回的 MS-Excel XML 然后会导致浏览器弹出打开/保存对话框。这必须适用于所有浏览器(如上所列)。
起初我尝试使用 Ajax 并使用文件名的“下载”属性创建一个动态锚, 但这仅适用于 5 种浏览器中的 3 种(FF、Chrome、Opera),不适用于 IE 或 Safari。 尝试以编程方式触发锚点的 Click 事件以导致实际的“下载”存在问题。
我最终做的是使用“不可见”的 IFRAME,它适用于所有 5 种浏览器!
所以这就是我想出的: [请注意,我绝不是 html/javascript 大师,仅包含相关代码]
HTML(相关位的sn-p)
<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# 服务器端(代码 sn-p) @Drew 创建了一个名为 XmlActionResult 的自定义 ActionResult,我根据自己的目的对其进行了修改。
Return XML from a controller's action in as an ActionResult?
我的控制器方法(返回 ActionResult)
创建修改后的 XmlActionResult 的实例并返回它
XmlActionResult 结果 = new XmlActionResult(excelXML, "application/vnd.ms-excel");
字符串版本 = DateTime.Now.ToString("dd_MMM_yyyy_hhmmsstt");
string fileMask = "LabelExport_{0}.xml";
result.DownloadFilename = string.Format(fileMask, version);
返回结果;
@Drew 创建的 XmlActionResult 类的主要修改。
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,而不是将其复制到新的?两个对象的生命周期相同。
ms.Position=0,您可以返回原始内存流。然后你可以return new FileStreamResult(ms,"text/xml");
answer from 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)
}
}
【讨论】:
使用其中一种方法
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;
}
【讨论】: