Rectangle 27 0

How to return XML in ASP.NET?


- doesn't follow the encoding specified
- doesn't set the XML declaration node to match
- wastes memory
<%@ 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; } }
}
<?xml version="1.0" encoding="UTF-8"?>
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
Response.ContentEncoding = ...
Response.ContentEncoding = System.Text.Encoding.UTF8;
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.Output
Response.OutputStream
Response.Write(doc.InnerXml);
Response.Write(doc.ToString());
doc.Save(Response.Output);
doc.Save(Response.OutputStream);
doc.Save(someTextWriter);

@RobKennedy You don't have to use text/xml, but you must use something. Otherwise it stays at the default text/html, which any browser worth it's salt will not display correctly when it encounters content that isn't any discernible html. Yes, you could also return application/xml. The point was, make sure your ContentType matches the actual content.

@RobKennedy You should not be using an aspx to return XML to the user-agent. By using an ASPX file you you forcing IIS to build up the whole WebForm infrastructure. As for the remainder of the .ashx file contents, i'll add that to the answer now.

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:

Do not use '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.

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.

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.

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.

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

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

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

The encoding given to the client in the header:

The question demands that answers must discuss which string encoding to use, but this answer only mentions UTF-8. Are other encodings allowed? The impression I get from this answer is that once we're using doc.Save(Response.Output), we can set Response.ContentEncoding to anything we want, and the system will ensure that the HTTP response header, the XML header, and the XML contents all agree. Is that correct?

This answer says the content type must be text/xml. Isn't it more nuanced than that, so that application/xml or image/svg+xml might be OK, too? In other words, doesn't the choice of content type depend on the content, or are you really saying that all XML returned via ASP.Net must use a single content type?

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.

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:

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

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:

must match the XML document's 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:

when the document is Saved:

would become

Note
Rectangle 27 0

How to return XML in ASP.NET?


Response.Charset = "UTF-8";
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetAllowResponseInBrowserHistory(true);
Response.Clear()
Response.ContentType = "text/xml"
Response.End()
Response.OutputStream
dom.Save(Response.Output);
dom.Save(Response.OutputStream);

@Ian: Care to explain? Works fine in the zillion places that I use it. If you really feel the need to take more control you would be better off creating a proper XmlTextWriter over the OutputStream and giving that to Save.

Definitely, a common client will not accept the content as XML without this content type present.

Does anyone have to worry about including "<?xml version="1.0" encoding="utf-8"?>" The doc.Save doesn't seem to do it.

I don't use this if you not sure you've dumped anything in the response already the go find it and get rid of it.

I don't worry about it I've not come across a parser that gets upset over its not being there. UTF-8 is the default.

Ideally you would use an ashx to send XML although I do allow code in an ASPX to intercept normal execution.

If you don't send cache headers some browsers (namely IE) will cache the response, subsequent requests will not necessarily come to the server. You also need to AllowResponseInBrowser if you want this to work over HTTPS (due to yet another bug in IE).

If you really have to in an ASPX but its a bit drastic, in an ASHX don't do it.

Let the response class handle building the content type header properly. Use UTF-8 unless you have a really, really good reason not to.

The XmlDocument object will automatically adjust its embedded encoding="..." encoding to that of the Response (e.g. UTF-8)

To send content of an XmlDocument simply use:

because it didn't update/change/check the encoding of the XmlDocument itself. If there is a mismatch, then the encoding of the xml won't match the encoding specified in the response.

Note
Rectangle 27 0

How to return XML in ASP.NET?


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

Below is the server side code that would call the handler and recieve the stream data and loads into xml doc

Note
Rectangle 27 0

How to return XML in ASP.NET?


' -----------------------------------------------------------------------------
   ' 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
   ' -----------------------------------------------------------------------------

Below is an example of the correct way I think. At least it is what I use. You need to do Response.Clear to get rid of any headers that are already populated. You need to pass the correct ContentType of text/xml. That is the way you serve xml. In general you want to serve it as charset UTF-8 as that is what most parsers are expecting. But I don't think it has to be that. But if you change it make sure to change your xml document declaration and indicate the charset in there. You need to use the XmlWriter so you can actually write in UTF-8 and not whatever charset is the default. And to have it properly encode your xml data in UTF-8.

Note