Я нашел правильный способ вернуть XML клиенту в ASP.NET. Я думаю, что если я укажу неправильные пути, это сделает правильный путь более понятным.
Неправильно:
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)
Использовать TextWriter
Do not use Response.OutputStream
Do Использование 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
Do use doc.Save(Response.Output);
Do not use doc.Save(Response.OutputStream);
Do not use 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
Полный пример
Роб Кеннеди был хорош тем, что мне не удалось включить пример от начала до конца.
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 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; } }
}