如何在 ASP.NET 中返回 XML?

Posted

技术标签:

【中文标题】如何在 ASP.NET 中返回 XML?【英文标题】:How to return XML in ASP.NET? 【发布时间】:2010-10-07 07:08:55 【问题描述】:

我遇到了许多在 ASP.NET 中返回 XML 的半解决方案。不过,我不想盲目地复制和粘贴一些在大多数情况下都能正常工作的代码;我想要 正确 代码,并且我想知道 为什么 它是正确的。我要批评;我想要信息;我要知识;我想要理解。

以下是代码片段,按照复杂度递增的顺序,代表我看到的一些部分解决方案,包括每个问题引起的一些进一步的问题,我想在这里回答。

一个彻底的答案必须说明为什么我们必须拥有或不能拥有以下任何东西,或者解释为什么它不相关。

Response.Clear(); Response.ContentType = "text/xml"; Response.ContentEncoding = Encoding.UTF8; Response.ContentEncoding = Encoding.UTF16; Response.ContentType = "text/xml; charset=utf-8"; Response.ContentType = "text/xml; charset=utf-16"; Response.End() 使用去掉前端文件内容的 aspx 使用 ashx 文件

最后,假设你需要像这样编写一个辅助函数的内容:

///<summary>Use this call inside your (Page_Xxx) method to write the
///xml to the web client. </summary>
///<remarks>See for https://***.com/questions/543319/how-to-return-xml-in-asp-net
///for proper usage.</remarks>
public static void ReturnXmlDocumentToWebClient(
    XmlDocument document,
    Page page)

   ...


我看到的每个解决方案都是从获取一个空的 aspx 页面开始,然后从前面的文件中删除所有 html(这会在 Visual Studio 中引起警告):

<%@ Page Language="C#"
      AutoEventWireup="true"
      CodeFile="GetTheXml.aspx.cs"
      Inherits="GetTheXml" %>

接下来我们使用Page_Load 事件写入输出:

protected void Page_Load(object sender, EventArgs e)

   String xml = "<foo>Hello, world!</foo>";

   Response.Write(xml);


我们是否需要将 ContentType 更改为 "text/xml"?即:

protected void Page_Load(object sender, EventArgs e)

   String xml = "<foo>Hello, world!</foo>";

   Response.ContentType = "text/xml";
   Response.Write(xml);


我们需要先打电话给Response.Clear吗?

protected void Page_Load(object sender, EventArgs e)

   String xml = "<foo>Hello, world!</foo>";

   Response.Clear();
   Response.ContentType = "text/xml";
   Response.Write(xml);

我们真的需要这样称呼吗? Response.Clear 是否不需要确保前面文件中的代码在 &lt;% ... %&gt; 之外为空(甚至不是空格或回车)的先前步骤?

Response.Clear 是否使它更健壮,以防有人在代码前端文件中留下空白行或空格?

使用 ashx 和空白 aspx 主文件一样吗,因为知道它不会输出 HTML?


我们需要打电话给Response.End吗?即:

protected void Page_Load(object sender, EventArgs e)

   String xml = "<foo>Hello, world!</foo>";

   Response.Clear();
   Response.ContentType = "text/xml";
   Response.Write(xml);
   Response.End();

Response.Write 之后还可能发生什么需要我们立即结束响应


text/xml 的 content-type 是否足够,或者应该改为 text/xml; charset=utf-8?

protected void Page_Load(object sender, EventArgs e)

   String xml = "<foo>Hello, world!</foo>";

   Response.Clear();
   Response.ContentType = "text/xml; charset=utf-8";
   Response.Write(xml);
   Response.End();

或者应该是吗?在内容类型中有一个字符集,但没有设置属性,会搞砸服务器吗?

为什么不用其他内容类型,例如:

UTF-8 utf-16 UTF-16

应该在Response.ContentEncoding中指定字符集吗?

protected void Page_Load(object sender, EventArgs e)

   String xml = "<foo>Hello, world!</foo>";

   Response.Clear();
   Response.ContentType = "text/xml";
   Response.ContentEncoding = Encoding.UTF8;
   Response.Write(xml);
   Response.End();

使用Response.ContentEncoding 是否比将其插入Response.ContentType 更好?是不是更糟?支持前者吗?是后者吗?


我实际上并不想写出字符串;我想写一个XmlDocument。 Someone suggests I can use the XmlWriter:

protected void Page_Load(object sender, EventArgs e)

   XmlDocument xml = GetXmlDocumentToShowTheUser();

   Response.Clear();
   Response.ContentType = "text/xml";
   Response.ContentEncoding = Encoding.UTF8;

   using (TextWriter textWriter = new StreamWriter(
         Response.OutputStream,
         Encoding.UTF8))
   
       XmlTextWriter xmlWriter = new XmlTextWriter(textWriter);
       // Write XML using xmlWriter
       //TODO: How to do this?
   

注意使用Response.OutputStream,而不是Response.Write。这个好吗?坏的?更好的?更差?快点?慢点?内存更密集?占用内存少?


我read你应该渲染

页面的 Render() 方法中的 XML 避免分块问题 使用 Page_Load() 时遇到。

什么是分块? 分块有哪些问题,使用 Page_Render 如何消除它们?


我不想将我的XmlDocument 对象的内容写入字符串然后再写入,因为这会浪费内存。也就是说,其中任何一个都是不好的:

Response.Write(doc.ToString());
Response.Write(doc.InnerXml);
xmlWrite.WriteString(doc.ToString());
xmlWrite.WriteString(doc.InnerXml);

类似问题

How to return XML in ASP.NET

参考文献

How Return XML From ASPX in ASP.NET 1.1

Writing XML output to an ASP.NET webpage

How do you output XML from ASP.NET?

Creating an ASHX handler in ASP.NET

【问题讨论】:

-1 有没有人向你解释:如何总结一个问题?并且,请问一些令人高兴的事情,而不是“某事”...... 那里,balexandre,它现在是一个社区 wiki。随时提供帮助。 几乎不可能理解你的问题究竟是什么。 嗯,我明白了。我在这里是因为我在问同样的问题,除了我确实想编写一个 XML 字符串的小例外。对围绕这个主题的歧义进行了很好的总结! 从不使用text/xml,没有这样的哑剧!提供 XML 时,您必须使用 mime application/xml 并具有 XML 命名空间:&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;&lt;p&gt;Example.&lt;/p&gt;&lt;/div&gt; 【参考方案1】:

我找到了在 ASP.NET 中将 XML 返回到客户端的正确方法。我想如果我指出错误的方式,它会让正确的方式更容易理解。

不正确:

Response.Write(doc.ToString());

不正确:

Response.Write(doc.InnerXml);

不正确:

Response.ContentType = "text/xml";
Response.ContentEncoding = System.Text.Encoding.UTF8;
doc.Save(Response.OutputStream);

正确:

Response.ContentType = "text/xml"; //Must be 'text/xml'
Response.ContentEncoding = System.Text.Encoding.UTF8; //We'd like UTF-8
doc.Save(Response.Output); //Save to the text-writer
      //using the encoding of the text-writer
      //(which comes from response.contentEncoding)

使用文本编写器

不要不要使用Response.OutputStream

不要使用Response.Output

两者都是流,但Output 是TextWriter。当XmlDocument 将自己保存到TextWriter 时,它将使用该TextWriter 指定的编码。 XmlDocument 将自动更改 xml 声明节点以匹配 TextWriter 使用的编码。例如在本例中为 XML 声明节点:

<?xml version="1.0" encoding="ISO-8859-1"?>

会变成

<?xml version="1.0" encoding="UTF-8"?>

这是因为 TextWriter 已设置为 UTF-8。 (稍后会详细介绍)。由于向 TextWriter 提供字符数据,它将使用适合其设置编码的字节序列对其进行编码。

不正确

doc.Save(Response.OutputStream);

在此示例中,文档被错误地保存到了 OutputStream,它不执行任何编码更改,并且可能与响应的内容编码或 XML 声明节点的指定编码不匹配。

正确

doc.Save(Response.Output);

XML 文档正确保存到 TextWriter 对象,确保正确处理编码。


设置编码

标头中给客户端的编码:

Response.ContentEncoding = ...

必须匹配 XML 文档的编码:

<?xml version="1.0" encoding="..."?>

必须与发送给客户端的字节序列中的实际编码相匹配。要使所有这三件事都一致,请设置单行:

Response.ContentEncoding = System.Text.Encoding.UTF8;

Response 对象上设置编码时,它会在 TextWriter 上设置相同的编码。 TextWriter 的编码集导致 XmlDocument 更改 xml 声明

<?xml version="1.0" encoding="UTF-8"?>

当文档被保存时:

doc.Save(someTextWriter);

保存到响应输出

您不想将文档保存为二进制流,或写入字符串:

不正确:

doc.Save(Response.OutputStream);

这里的 XML 被错误地保存为二进制流。最终的字节编码序列与 XML 声明或网络服务器响应的内容编码不匹配。

不正确:

Response.Write(doc.ToString());
Response.Write(doc.InnerXml);

这里的 XML 被错误地转换为没有编码的字符串。 XML 声明节点未更新以反映响应的编码,并且响应未正确编码以匹配响应的编码。此外,将 XML 存储在中间字符串中会浪费内存。

不想将 XML 保存为字符串,或者将 XML 填充为字符串和 response.Write 字符串,因为:

- doesn't follow the encoding specified
- doesn't set the XML declaration node to match
- wastes memory

不要使用doc.Save(Response.Output);

不要不要使用doc.Save(Response.OutputStream);

不要不要使用Response.Write(doc.ToString());

不要不要使用'Response.Write(doc.InnerXml);`


设置内容类型

响应的 ContentType 必须设置为 "text/xml"。否则,客户端将不知道您正在向其发送 XML。

最终答案

Response.Clear(); //Optional: if we've sent anything before
Response.ContentType = "text/xml"; //Must be 'text/xml'
Response.ContentEncoding = System.Text.Encoding.UTF8; //We'd like UTF-8
doc.Save(Response.Output); //Save to the text-writer
    //using the encoding of the text-writer
    //(which comes from response.contentEncoding)
Response.End(); //Optional: will end processing

完整示例

Rob Kennedy 的优点是我没有包含从头到尾的示例。

GetPatronInformation.ashx

<%@ WebHandler Language="C#" Class="Handler" %>

using System;
using System.Web;
using System.Xml;
using System.IO;
using System.Data.Common;

//Why a "Handler" and not a full ASP.NET form?
//Because many people online critisized my original solution
//that involved the aspx (and cutting out all the HTML in the front file),
//noting the overhead of a full viewstate build-up/tear-down and processing,
//when it's not a web-form at all. (It's a pure processing.)

public class Handler : IHttpHandler

   public void ProcessRequest(HttpContext context)
   
      //GetXmlToShow will look for parameters from the context
      XmlDocument doc = GetXmlToShow(context);

      //Don't forget to set a valid xml type.
      //If you leave the default "text/html", the browser will refuse to display it correctly
      context.Response.ContentType = "text/xml";

      //We'd like UTF-8.
      context.Response.ContentEncoding = System.Text.Encoding.UTF8;
      //context.Response.ContentEncoding = System.Text.Encoding.UnicodeEncoding; //But no reason you couldn't use UTF-16:
      //context.Response.ContentEncoding = System.Text.Encoding.UTF32; //Or UTF-32
      //context.Response.ContentEncoding = new System.Text.Encoding(500); //Or EBCDIC (500 is the code page for IBM EBCDIC International)
      //context.Response.ContentEncoding = System.Text.Encoding.ASCII; //Or ASCII
      //context.Response.ContentEncoding = new System.Text.Encoding(28591); //Or ISO8859-1
      //context.Response.ContentEncoding = new System.Text.Encoding(1252); //Or Windows-1252 (a version of ISO8859-1, but with 18 useful characters where they were empty spaces)

      //Tell the client don't cache it (it's too volatile)
      //Commenting out NoCache allows the browser to cache the results (so they can view the XML source)
      //But leaves the possiblity that the browser might not request a fresh copy
      //context.Response.Cache.SetCacheability(HttpCacheability.NoCache);

      //And now we tell the browser that it expires immediately, and the cached copy you have should be refreshed
      context.Response.Expires = -1;

      context.Response.Cache.SetAllowResponseInBrowserHistory(true); //"works around an Internet&nbsp;Explorer bug"

      doc.Save(context.Response.Output); //doc saves itself to the textwriter, using the encoding of the text-writer (which comes from response.contentEncoding)

      #region Notes
      /*
       * 1. Use Response.Output, and NOT Response.OutputStream.
       *  Both are streams, but Output is a TextWriter.
       *  When an XmlDocument saves itself to a TextWriter, it will use the encoding
       *  specified by the TextWriter. The XmlDocument will automatically change any
       *  XML declaration node, i.e.:
       *     <?xml version="1.0" encoding="ISO-8859-1"?>
       *  to match the encoding used by the Response.Output's encoding setting
       * 2. The Response.Output TextWriter's encoding settings comes from the
       *  Response.ContentEncoding value.
       * 3. Use doc.Save, not Response.Write(doc.ToString()) or Response.Write(doc.InnerXml)
       * 3. You DON'T want to save the XML to a string, or stuff the XML into a string
       *  and response.Write that, because that
       *   - doesn't follow the encoding specified
       *   - wastes memory
       *
       * To sum up: by Saving to a TextWriter: the XML Declaration node, the XML contents,
       * and the HTML Response content-encoding will all match.
       */
      #endregion Notes
   

   private XmlDocument GetXmlToShow(HttpContext context)
   
      //Use context.Request to get the account number they want to return
      //GET /GetPatronInformation.ashx?accountNumber=619

      //Or since this is sample code, pull XML out of your rear:
      XmlDocument doc = new XmlDocument();
      doc.LoadXml("<Patron><Name>Rob Kennedy</Name></Patron>");

      return doc;
   

   public bool IsReusable  get  return false;  

【讨论】:

问题要求答案必须讨论使用哪种字符串编码,但这个答案只提到了 UTF-8。是否允许其他编码?我从这个答案中得到的印象是,一旦我们使用doc.Save(Response.Output),我们可以将Response.ContentEncoding 设置为任何我们想要的,系统将确保HTTP 响应头、XML 头和XML 内容都一致.对吗? 这个答案说内容类型必须是 text/xml。是不是比这更微妙,所以 application/xml 或 image/svg+xml 也可能没问题?换句话说,内容类型的选择不是取决于content,还是你真的说all XML通过ASP.Net返回must i> 使用单一内容类型? 这个答案似乎没有讨论 aspx 或 ashx 文件,或者它们应该或不应该包含的内容。 @RobKennedy 您不应该使用 aspx 将 XML 返回给用户代理。通过使用 ASPX 文件,您可以强制 IIS 构建整个 WebForm 基础结构。至于.ashx 文件内容的其余部分,我现在将其添加到答案中。 @RobKennedy 你不必使用text/xml,但你必须使用something。否则它会停留在默认的text/html,当它遇到不是任何可识别的 html 的内容时,任何值得它的盐的浏览器都不会正确显示。是的,您也可以返回application/xml。重点是,确保您的 ContentType 与实际内容匹配。【参考方案2】:

虽然我确实允许 ASPX 中的代码拦截正常执行,但理想情况下您会使用 ashx 发送 XML。

Response.Clear()

如果你不确定你已经在响应中倾倒了任何东西,我不会使用它,然后去找它并摆脱它。

Response.ContentType = "text/xml"

毫无疑问,如果没有这种内容类型,普通客户端不会接受 XML 格式的内容。

 Response.Charset = "UTF-8";

让响应类正确处理构建内容类型标头。除非您有非常非常好的理由不这样做,否则请使用 UTF-8。

Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetAllowResponseInBrowserHistory(true);

如果你不发送缓存头,一些浏览器(即IE)会缓存响应,后续请求不一定会到达服务器。如果您希望它通过 HTTPS 工作,您还需要 AllowResponseInBrowser(由于 IE 中的另一个错误)。

要发送 XmlDocument 的内容,只需使用:

罢工>

dom.Save(Response.OutputStream);

dom.Save(Response.Output);

只要确保编码匹配,(使用 UTF-8 的另一个好理由)。

XmlDocument 对象会自动将其嵌入的encoding="..." 编码调整为Response 的编码(例如UTF-8

Response.End()

如果你真的需要在 ASPX 中但它有点激烈,在 ASHX 中不要这样做。

【讨论】:

有没有人需要担心包含 "" 的 doc.Save 好像不行。 我不担心我没有遇到一个解析器因为它不在那里而感到不安。 UTF-8 是默认值。 这个例子错误地使用了Response.OutputStream(一个Stream)而不是`Response.Output'(一个TextWriter) @Ian:想解释一下吗?在我使用它的无数地方都可以正常工作。如果您真的觉得需要进行更多控制,最好在 OutputStream 上创建一个适当的 XmlTextWriter 并将其提供给 Save。 因为它没有更新/更改/检查 XmlDocument 本身的编码。如果不匹配,则 xml 的编码将与响应中指定的编码不匹配。【参考方案3】:

以下是我认为的正确方法的示例。至少这是我使用的。 您需要执行 Response.Clear 以摆脱任何已填充的标头。 您需要传递正确的 text/xml 的 ContentType。这就是您提供 xml 的方式。 通常,您希望将其作为字符集 UTF-8 提供,因为这是大多数解析器所期望的。但我认为不必如此。但是,如果您更改它,请确保更改您的 xml 文档声明并在其中指明字符集。 您需要使用 XmlWriter,这样您才能真正使用 UTF-8 而不是默认的任何字符集。并让它以 UTF-8 正确编码您的 xml 数据。

   ' -----------------------------------------------------------------------------
   ' OutputDataSetAsXML
   '
   ' Description: outputs the given dataset as xml to the response object
   '
   ' Arguments:
   '    dsSource           - source data set
   '
   ' Dependencies:
   '
   ' History
   ' 2006-05-02 - WSR : created
   '
   Private Sub OutputDataSetAsXML(ByRef dsSource As System.Data.DataSet)

      Dim xmlDoc As System.Xml.XmlDataDocument
      Dim xmlDec As System.Xml.XmlDeclaration
      Dim xmlWriter As System.Xml.XmlWriter

      ' setup response
      Me.Response.Clear()
      Me.Response.ContentType = "text/xml"
      Me.Response.Charset = "utf-8"
      xmlWriter = New System.Xml.XmlTextWriter(Me.Response.OutputStream, System.Text.Encoding.UTF8)

      ' create xml data document with xml declaration
      xmlDoc = New System.Xml.XmlDataDocument(dsSource)
      xmlDoc.DataSet.EnforceConstraints = False
      xmlDec = xmlDoc.CreateXmlDeclaration("1.0", "UTF-8", Nothing)
      xmlDoc.PrependChild(xmlDec)

      ' write xml document to response
      xmlDoc.WriteTo(xmlWriter)
      xmlWriter.Flush()
      xmlWriter.Close()
      Response.End()

   End Sub
   ' -----------------------------------------------------------------------------

【讨论】:

【参考方案4】:

这里似乎至少有 10 个问题合而为一,只有几点。

Response.Clear - 这实际上取决于应用程序中发生的其他事情 - 如果您在管道的早期有 httpmodules 可能正在编写您不想要的东西 - 然后清除它。测试它并找出答案。 Fiddler 或 Wireshark 对此很有用。

内容类型为 text/xml - 是的 - 好主意 - 阅读 HTTP 规范,了解为什么这很重要。 IMO 任何从事网络工作的人都应该至少阅读 1.0 和 1.1 规范一次。

编码 - 你的 xml 是如何编码的 - 如果是 utf-8,那么就这样说,如果不是,说其他合适的,只要确保它们都匹配。

Page - 就个人而言,会使用 ashx 或 httpmodule,如果您正在使用 page,并且希望它更快一点,请摆脱 autoeventwireup 并手动绑定事件处理程序。

首先将 xml 转储到字符串中可能会有点浪费内存,但这很大程度上取决于 xml 的大小,您是否会注意到。

正如其他人所建议的那样,将 xml 保存到输出流可能是最快的,我通常会这样做,但如果您不确定,请对其进行测试,不要依赖您在互联网上阅读的内容。不要只相信我说的任何话。

对于另一种方法,如果 xml 没有太大变化,您可以将其写入磁盘并直接提供文件,这可能会非常高效,但就像编程中的所有内容一样,这取决于...

【讨论】:

【参考方案5】:

令我惊讶的是,似乎没有人提到您可以使用 .NET 4.0 中提供的 XDocument / XElement 并使其更容易输出 XML。

【讨论】:

【参考方案6】:

以下是调用处理程序并接收流数据并加载到 xml 文档中的服务器端代码

 Stream stream = null;
       **Create a web request with the specified URL**
        WebRequest myWebRequest = WebRequest.Create(@"http://localhost/XMLProvider/XMLProcessorHandler.ashx");
        **Senda a web request and wait for response.**
        WebResponse webResponse = myWebRequest.GetResponse();
        **Get the stream object from response object**
        stream = webResponse.GetResponseStream();

       XmlDocument xmlDoc = new XmlDocument();
      **Load stream data into xml**
       xmlDoc.Load(stream);

【讨论】:

【参考方案7】:

以下是处理程序将在服务器端返回包含 xml 数据的流数据的方式。

这是返回数据的处理程序代码。

    public void ProcessRequest(HttpContext context)
    

        StringBuilder xmlBuilder = new StringBuilder();

        xmlBuilder.Append("<Names>");
        xmlBuilder.Append("<Name>");
        xmlBuilder.Append("Sheo");
        xmlBuilder.Append("</Name>");
        xmlBuilder.Append("</Names>");
        context.Response.ContentType = "application/octet-stream";
        context.Response.BinaryWrite(Encoding.UTF8.GetBytes(xmlBuilder.ToString()));
        context.Response.End();

    

【讨论】:

【参考方案8】:
XmlDocument xd = new XmlDocument();
xd.LoadXml(xmlContent);

context.Response.Clear();
context.Response.ContentType = "text/xml";
context.Response.ContentEncoding = System.Text.Encoding.UTF8;
xd.Save(context.Response.Output);
context.Response.Flush();
context.Response.SuppressContent = true;
context.ApplicationInstance.CompleteRequest();

【讨论】:

【参考方案9】:

您基本上已经回答了所有问题,所以我不确定这里的重点是什么?

FWIW I 将使用 httphandler - 调用页面生命周期并不得不处理剪掉视图状态和会话的部分以及你有什么没有意义似乎没有意义XML 文档。这就像买一辆汽车,然后把它拆成零件来制造你的摩托车。

内容类型很重要,它是请求者知道如何处理响应的方式。

【讨论】:

我发布的各种内容来自我在互联网上找到的各种解决方案。在 SO 上,我们有两个答案和两种方法。 在我发布该评论时,这个问题有两个答案,有两种截然不同的方法。截至目前(上午 10:30),共有三种方法。我希望每个发布“他们的”解决方案的人也能解释为什么另一个解决方案是“错误的”。 我的意思是,我可能应该创建一个新问题,“在 ASP.net 中返回 XML 的最佳方式是什么”,并发布我在互联网上找到的 10 种不同的解决方案, 4 我的问题中有 3 个答案,然后让人们专注于决定正确的方法。 我投了反对票,因为“你基本上回答了所有问题”。我没有回答任何事情。复制粘贴我从互联网上找到的其他人的解决方案对答案没有帮助 - 它只是表明我投入了研究工作。从那以后,我了解到为什么 all 我最初问题中的示例是错误的;给出无效的 xml。直到有人指出这些示例中的每一个都有什么错误,人们才会认为它们是有效的。我自己接受的答案详细说明了其他解决方案的问题。

以上是关于如何在 ASP.NET 中返回 XML?的主要内容,如果未能解决你的问题,请参考以下文章

如何指定在 ASP.NET Web API 中是需要 JSON 还是 XML?

如何使用ASP.Net发送HTTP请求并且获取返回的XML

如何在 asp.net 中创建 RESTful Web 服务?

ASP.NET Core 1.0 Web API 不返回 XML

如何在 ASP.NET MVC 中向客户端发送 XML 文件

如何在 POST 正文中为 asp.net WebAPI POST 路由格式化 XML