Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
362 views
in Technique[技术] by (71.8m points)

c# - How to return XML in ASP.NET?

I have encountered many half-solutions to the task of returning XML in ASP.NET. I don't want to blindly copy & paste some code that happens to work most of the time, though; I want the right code, and I want to know why it's right. I want criticism; I want information; I want knowledge; I want understanding.

Below are code fragments, in order of increasing complexity, representing some of the partial solutions I've seen, including some of the further questions each one causes, and which I'd like to have answered here.

A thorough answer must address why we must have or must not have any of the following things, or else explain why it's irrelevant.

  • 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()
  • Using an aspx with the front-file guts ripped out
  • Using an ashx file

In the end, imagine you need to write the contents of a helper function like this:

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

Every solution I see starts with taking an empty aspx page, and trimming all the HTML out of the front file (which causes warnings in Visual Studio):

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

Next we use the Page_Load event to write to the output:

protected void Page_Load(object sender, EventArgs e)
{
   String xml = "<foo>Hello, world!</foo>";

   Response.Write(xml);
}

Do we need to change the ContentType to "text/xml"? I.e.:

protected void Page_Load(object sender, EventArgs e)
{
   String xml = "<foo>Hello, world!</foo>";

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

Do we need to call Response.Clear first?

protected void Page_Load(object sender, EventArgs e)
{
   String xml = "<foo>Hello, world!</foo>";

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

Do we really need to call that? Doesn't Response.Clear make the prior step of making sure that the code in the front file was empty (not even a space or a carriage return) outside of the <% ... %> unnecessary?

Does Response.Clear make it more robust, in case someone left a blank line or space in the code-front file?

Is using ashx the same as a blank aspx main file, because it's understood that it's not going to output HTML?


Do we need to call Response.End? I.e.:

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

What else could possibly happen after Response.Write that needs us to end the response right now?


Is the content-type of text/xml sufficient, or should it instead be 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();
}

Or should it specifically not be that? Does having a charset in the content type, but not setting the property, screw up the server?

Why not some other content type, e.g.:

  • UTF-8
  • utf-16
  • UTF-16

Should the charset be specified in 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();
}

Is using Response.ContentEncoding better than jamming it into Response.ContentType? Is it worse? Is the former supported? Is the latter?


I don't actually want to write a String out; I want to write out an 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?
   }
}

Note the use of Response.OutputStream, rather than Response.Write. Is this good? Bad? Better? Worse? Faster? Slower? More memory intensive? Less memory intensive?


I read that you should render

the XML in the page’s Render() method to avoid problems with chunking encountered when using Page_Load().

What is chunking? What are the problems with chunking, and how does using using Page_Render eliminate them?


I don't want to write the contents of my XmlDocument object into a string and then write that because that wastes memory. That is, any of these would be bad:

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

Similar Questions

How to return XML in ASP.NET

References

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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I've found the proper way to return XML to a client in ASP.NET. I think if I point out the wrong ways, it will make the right way more understandable.

Incorrect:

Response.Write(doc.ToString());

Incorrect:

Response.Write(doc.InnerXml);

Incorrect:

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

Correct:

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)

Use a TextWriter

Do not use Response.OutputStream

Do use Response.Output

Both are streams, but Output is a TextWriter. When an XmlDocument saves itself to a TextWriter, it will use the encoding specified by that TextWriter. The XmlDocument will automatically change the xml declaration node to match the encoding used by the TextWriter. e.g. in this case the XML declaration node:

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

would become

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

This is because the TextWriter has been set to UTF-8. (More on this in a moment). As the TextWriter is fed character data, it will encode it with the byte sequences appropriate for its set encoding.

Incorrect:

doc.Save(Response.OutputStream);

In this example the document is incorrectly saved to the OutputStream, which performs no encoding change, and may not match the response's content-encoding or the XML declaration node's specified encoding.

Correct

doc.Save(Response.Output);

The XML document is correctly saved to a TextWriter object, ensuring the encoding is properly handled.


Set Encoding

The encoding given to the client in the header:

Response.ContentEncoding = ...

must match the XML document's encoding:

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

must match the actual encoding present in the byte sequences sent to the client. To make all three of these things agree, set the single line:

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

When the encoding is set on the Response object, it sets the same encoding on the TextWriter. The encoding set of the TextWriter causes the XmlDocument to change the xml declaration:

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

when the document is Saved:

doc.Save(someTextWriter);

Save to the response Output

You do not want to save the document to a binary stream, or write a string:

Incorrect:

doc.Save(Response.OutputStream);

Here the XML is incorrectly saved to a binary stream. The final byte encoding sequence won't match the XML declaration, or the web-server response's content-encoding.

Incorrect:

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

Here the XML is incorrectly converted to a string, which does not have an encoding. The XML declaration node is not updated to reflect the encoding of the response, and the response is not properly encoded to match the response's encoding. Also, storing the XML in an intermediate string wastes memory.

You don't want to save the XML to a string, or stuff the XML into a string and response.Write a string, because that:

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

Do use doc.Save(Response.Output);

Do not use doc.Save(Response.OutputStream);

Do not use Response.Write(doc.ToString());

Do not use 'Response.Write(doc.InnerXml);`


Set the content-type

The Response's ContentType must be set to "text/xml". If not, the client will not know you are sending it XML.

Final Answer

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

Complete Example

Rob Kennedy had the good point that I failed to include the start-to-finish example.

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...