Подтвердить что ты не робот

Как вернуть XML в ASP.NET?

Я сталкивался со многими половинными решениями задачи возврата XML в ASP.NET. Однако я не хочу слепо копировать и вставлять код, который работает в большинстве случаев; Я хочу правильный код и хочу знать, почему он правильный. Я хочу критики; Я хочу информацию; Я хочу знания; Я хочу понимания.

Ниже приведены фрагменты кода в порядке возрастания сложности, представляющие некоторые частичные решения, которые я видел, включая некоторые дополнительные вопросы, которые вызывает каждый из них, и на которые я бы хотел ответить здесь.

Тщательный ответ должен касаться того, почему мы должны иметь или не иметь какой-либо из следующих вещей, или объяснять, почему это не имеет значения.

  • 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://stackoverflow.com/info/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 не делает предыдущий шаг, чтобы убедиться, что код в переднем файле был пуст (даже без пробела или возврата каретки) за пределами <%... %> ненужного?

Делает ли 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 достаточным или вместо него должен быть 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. Кто-то предлагает мне использовать 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. Это хорошо? Плохой? Лучше? Хуже? Быстрее? Помедленнее? Больше памяти? Меньше памяти?


Я прочитал, что вы должны сделать

XML в методе Render() страниц, чтобы избежать проблем с чанкингом, возникающими при использовании Page_Load().

Что такое чанкинг? Каковы проблемы с разбиением на фрагменты и как их использование с помощью Page_Render устраняет их?


Я не хочу записывать содержимое моего объекта XmlDocument в строку, а затем записывать это, потому что это тратит впустую память. То есть любой из них будет плох:

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

Подобные вопросы

Как вернуть XML в ASP.NET

Рекомендации

Как вернуть XML из ASPX в ASP.NET 1.1

Запись вывода XML на веб-страницу ASP.NET

Как вы выводите XML из ASP.NET?

Создание обработчика ASHX в ASP.NET

4b9b3361

Ответ 1

Я нашел правильный способ вернуть 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

Использовать не Response.OutputStream

Использовать Response.Output

Оба являются потоками, но Output является TextWriter. Когда XmlDocument сохраняет себя TextWriter, он будет использовать кодировку, указанную этим TextWriter. XmlDocument автоматически изменит объявление xml node в соответствии с кодировкой, используемой TextWriter. например в этом случае объявление XML node:

<?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 node указанной кодировки.

Правильно

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 node не обновляется, чтобы отражать кодировку ответа, и ответ неправильно кодируется в соответствии с кодировкой ответа. Кроме того, хранение 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());

Do not использовать '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 not a web-form at all. (It 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 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 encoding setting
       * 2. The Response.Output TextWriter 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; } }
}

Ответ 2

В идеале вы должны использовать ashx для отправки XML, хотя я разрешаю код в ASPX для перехвата нормального выполнения.

Response.Clear()

Я не использую это, если вы не уверены, что вы сбросили что-либо в ответ, уже нашли его и избавитесь от него.

Response.ContentType = "text/xml"

Определенно, общий клиент не будет принимать контент как XML без этого типа содержимого.

 Response.Charset = "UTF-8";

Пусть класс ответа обрабатывает заголовок заголовка содержимого правильно. Используйте UTF-8, если у вас нет действительно хорошей причины, чтобы не делать этого.

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

Если вы не отправляете кеш-заголовки, некоторые браузеры (а именно IE) будут кэшировать ответ, последующие запросы не обязательно будут поступать на сервер. Вам также необходимо AllowResponseInBrowser, если вы хотите, чтобы это работало по HTTPS (из-за еще одной ошибки в IE).

Для отправки содержимого XmlDocument просто используйте:

dom.Save(Response.OutputStream);

Забастовкa >

dom.Save(Response.Output);

Просто убедитесь, что кодировки совпадают (еще одна веская причина использовать UTF-8).

Объект XmlDocument автоматически настроит свою внедренную encoding="..." кодировку на Response (например, UTF-8)

Response.End()

Если вам действительно нужно в ASPX, но это немного радикально, в ASHX этого не делать.

Ответ 3

Ниже приведен пример правильного пути, который я думаю. По крайней мере, это то, что я использую. Вам нужно выполнить Response.Clear, чтобы избавиться от каких-либо заголовков, которые уже заполнены. Вам нужно передать правильный ContentType text/xml. Так вы подаете xml. В общем, вы хотите использовать его в качестве кодировки UTF-8, поскольку это то, что ожидают большинство парсеров. Но я не думаю, что это должно быть так. Но если вы измените его, обязательно измените декларацию документа xml и укажите там кодировку. Вам нужно использовать XmlWriter, чтобы вы могли писать в UTF-8, а не по умолчанию. И чтобы он правильно кодировал ваши XML-данные в UTF-8.

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

Тип содержимого для текста /xml - yup - хорошая идея - прочитайте спецификацию HTTP о том, почему это важно. IMO, кто занимается веб-работой, должен хотя бы раз читать спецификации 1.0 и 1.1.

Кодирование - как ваш XML-код закодирован - если это utf-8, тогда скажите так, если нет, скажите что-то еще подходящее, просто убедитесь, что все они совпадают.

Страница - лично, будет использовать ashx или httpmodule, если вы используете страницу и хотите ее немного быстрее, избавитесь от autoeventwireup и вручную привяжите обработчики событий.

Вероятно, это будет немного пустой тратой памяти, чтобы сначала выгрузить xml в строку, но это зависит от размера xml, независимо от того, заметили ли вы когда-нибудь.

Как утверждали другие, сохранение xml в выходной поток, вероятно, самый быстрый, я бы это сделал нормально, но если вы не уверены, протестируйте его, не полагайтесь на то, что вы читаете на interweb. Не верите всему, что я говорю.

Для другого подхода, если xml не так сильно изменится, вы можете просто записать его на диск и напрямую передать файл, что, скорее всего, будет довольно показательным, но, как и все в программировании, это зависит...

Ответ 5

Я удивлен, что никто, кажется, никогда не упоминал, что вы можете использовать XDocument/XElement, которые доступны в .NET 4.0, и сделать намного проще вывод XML.

Ответ 6

Ниже приведен способ, с помощью которого обработчик будет возвращать данные потока, которые будут содержать 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();

    }

Ответ 7

Ниже приведен код на стороне сервера, который вызывается обработчиком и получает данные потока и загружается в документ 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);

Ответ 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 Я бы использовал httphandler - кажется, нет смысла вызывать жизненный цикл страницы и иметь дело с отсечением бит viewstate и session и что у вас нет смысла для XML-документа. Это как покупка автомобиля и снятие его на части, чтобы сделать ваш мотоцикл.

И контент-тип важен, так как запрашивающий знает, что делать с ответом.