Cómo devolver XML en ASP.NET?
(8)
A continuación hay un ejemplo de la forma correcta en que pienso. Al menos es lo que uso. Debe hacer una Respuesta. Eliminar para eliminar los encabezados que ya están llenos. Debe pasar el ContentType correcto de texto / xml. Esa es la forma en que sirve xml. En general, desea servirlo como el juego de caracteres UTF-8, ya que eso es lo que la mayoría de los analizadores esperan. Pero no creo que tenga que ser eso. Pero si lo cambia, asegúrese de cambiar su declaración de documento xml e indique el juego de caracteres allí. Necesita utilizar el XmlWriter para que pueda escribir en UTF-8 y no sea el charset predeterminado. Y para que codifique correctamente sus datos xml en 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
'' -----------------------------------------------------------------------------
He encontrado muchas soluciones a medias para la tarea de devolver XML en ASP.NET. No obstante, no quiero copiar y pegar ciegamente algún código que funcione la mayor parte del tiempo; Quiero el código correcto , y quiero saber por qué es correcto. Quiero críticas Quiero información; Quiero conocimiento; Quiero entendimiento.
A continuación se encuentran los fragmentos de código, en orden creciente de complejidad, que representan algunas de las soluciones parciales que he visto, incluidas algunas de las preguntas adicionales que cada una provoca y que me gustaría que respondieran aquí.
Una respuesta exhaustiva debe abordar por qué debemos tener o no tener cualquiera de las siguientes cosas, o bien explicar por qué es irrelevante.
- 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 ()
- Usando un aspx con las tripas del archivo frontal arrancadas
- Usando un archivo ashx
Al final, imagine que necesita escribir el contenido de una función auxiliar de esta manera:
///<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)
{
...
}
Cada solución que veo comienza con tomar una página aspx vacía y recortar todo el HTML del archivo frontal (que causa advertencias en Visual Studio):
<%@ Page Language="C#"
AutoEventWireup="true"
CodeFile="GetTheXml.aspx.cs"
Inherits="GetTheXml" %>
A continuación, usamos el evento Page_Load
para escribir en el resultado:
protected void Page_Load(object sender, EventArgs e)
{
String xml = "<foo>Hello, world!</foo>";
Response.Write(xml);
}
¿Necesitamos cambiar ContentType a "text / xml" ? Es decir:
protected void Page_Load(object sender, EventArgs e)
{
String xml = "<foo>Hello, world!</foo>";
Response.ContentType = "text/xml";
Response.Write(xml);
}
¿Necesitamos llamar primero a 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);
}
¿De verdad necesitamos llamar eso? ¿ Response.Clear
no hace el paso previo de asegurarse de que el código en el archivo frontal estaba vacío (ni siquiera un espacio o un retorno de carro) fuera del <% ... %>
innecesario?
¿ Response.Clear
hace más robusto, en caso de que alguien deje una línea en blanco o espacio en el archivo de código de entrada?
¿Usar ashx es lo mismo que un archivo principal vacío de aspx, porque se entiende que no va a generar HTML?
¿Necesitamos llamar a Response.End
? Es decir:
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();
}
¿Qué más podría suceder después de Response.Write
que nos necesita para finalizar la respuesta en este momento ?
Es el tipo de contenido de text/xml
suficiente, o debería ser texto / 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();
}
¿O debería específicamente no ser eso? ¿Tener un juego de caracteres en el tipo de contenido, pero no configurar la propiedad, arruina el servidor?
¿Por qué no otro tipo de contenido, por ejemplo:
- UTF-8
- utf-16
- UTF-16
¿Debería especificarse el juego de caracteres en 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();
}
¿Está utilizando Response.ContentEncoding
mejor que interbloquearlo en Response.ContentType
? ¿Es peor? ¿El primero es compatible? Es este último?
Realmente no quiero escribir una String; Quiero escribir un XmlDocument
. Alguien sugiere que pueda usar el 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?
}
}
Tenga en cuenta el uso de Response.OutputStream
, en lugar de Response.Write
. ¿Es esto bueno? ¿Malo? ¿Mejor? ¿Peor? ¿Más rápido? ¿Más lento? Más memoria intensiva? Menos memoria intensiva?
read que debes renderizar
el XML en el método Render () de la página para evitar problemas con la fragmentación encontrada al usar Page_Load ().
¿Qué es fragmentar ? ¿Cuáles son los problemas con la fragmentación, y cómo el uso de Page_Render
elimina?
No quiero escribir el contenido de mi objeto XmlDocument
en una cadena y luego escribir eso porque eso desperdicia memoria. Es decir, cualquiera de estos sería malo:
Response.Write(doc.ToString());
Response.Write(doc.InnerXml);
xmlWrite.WriteString(doc.ToString());
xmlWrite.WriteString(doc.InnerXml);
Preguntas similares
Referencias
Cómo devolver XML desde ASPX en ASP.NET 1.1
Escritura de salida XML en una página web ASP.NET
Básicamente ya has respondido algo y todo, ¿por qué no estoy seguro de cuál es el punto aquí?
FWIW Usaría un httphandler: no parece tener sentido invocar un ciclo de vida de página y tener que lidiar con el recorte de los bits de viewstate y sesión, y qué tiene usted que no tiene sentido para un documento XML. Es como comprar un automóvil y quitarle partes para hacer tu moto.
Y el tipo de contenido es muy importante, es cómo el solicitante sabe qué hacer con la respuesta.
Encontré la forma correcta de devolver XML a un cliente en ASP.NET. Creo que si señalo el camino equivocado, será más comprensible de la manera correcta.
Incorrecto:
Response.Write(doc.ToString());
Incorrecto:
Response.Write(doc.InnerXml);
Incorrecto:
Response.ContentType = "text/xml";
Response.ContentEncoding = System.Text.Encoding.UTF8;
doc.Save(Response.OutputStream);
Correcto:
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 un TextWriter
No use Response.OutputStream
Use Response.Output
Ambas son transmisiones, pero Output
es un TextWriter . Cuando un XmlDocument
guarda en un TextWriter , usará la codificación especificada por ese TextWriter. XmlDocument cambiará automáticamente el nodo de declaración xml para que coincida con la codificación utilizada por TextWriter. por ejemplo, en este caso, el nodo de declaración XML:
<?xml version="1.0" encoding="ISO-8859-1"?>
se convertiría
<?xml version="1.0" encoding="UTF-8"?>
Esto se debe a que TextWriter se ha configurado en UTF-8. (Más sobre esto en un momento). A medida que TextWriter recibe datos de caracteres, los codificará con las secuencias de bytes apropiadas para su codificación establecida.
Incorrecto :
doc.Save(Response.OutputStream);
En este ejemplo, el documento se guarda incorrectamente en OutputStream, que no realiza ningún cambio de codificación, y puede no coincidir con la codificación de contenido de la respuesta o la codificación especificada del nodo de declaración XML.
Correcto
doc.Save(Response.Output);
El documento XML se guarda correctamente en un objeto TextWriter, asegurando que la codificación se maneje correctamente.
Establecer codificación
La codificación dada al cliente en el encabezado:
Response.ContentEncoding = ...
debe coincidir con la codificación del documento XML:
<?xml version="1.0" encoding="..."?>
debe coincidir con la codificación real presente en las secuencias de bytes enviadas al cliente. Para que las tres cosas estén de acuerdo, establece la línea única:
Response.ContentEncoding = System.Text.Encoding.UTF8;
Cuando la codificación se establece en el objeto Response , establece la misma codificación en el TextWriter . El conjunto de codificación del TextWriter hace que XmlDocument cambie la declaración xml :
<?xml version="1.0" encoding="UTF-8"?>
cuando el documento está guardado:
doc.Save(someTextWriter);
Guardar en la salida de respuesta
No desea guardar el documento en una secuencia binaria o escribir una cadena:
Incorrecto:
doc.Save(Response.OutputStream);
Aquí el XML se guarda incorrectamente en una secuencia binaria. La secuencia de codificación de byte final no coincidirá con la declaración XML o la codificación de contenido de la respuesta del servidor web.
Incorrecto:
Response.Write(doc.ToString());
Response.Write(doc.InnerXml);
Aquí el XML se convierte incorrectamente en una cadena, que no tiene una codificación. El nodo de declaración XML no se actualiza para reflejar la codificación de la respuesta, y la respuesta no está codificada correctamente para que coincida con la codificación de la respuesta. Además, almacenar el XML en una cadena intermedia desperdicia memoria.
No desea guardar el XML en una cadena, ni rellenar el XML en una cadena y response.Write
Escriba una cadena, porque eso:
- doesn''t follow the encoding specified
- doesn''t set the XML declaration node to match
- wastes memory
Utilice doc.Save(Response.Output);
No use doc.Save(Response.OutputStream);
No use Response.Write(doc.ToString());
No use ''Response.Write (doc.InnerXml); `
Establecer el tipo de contenido
ContentType de la respuesta se debe establecer en "text/xml"
. De lo contrario, el cliente no sabrá que lo está enviando XML.
Respuesta final
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
Ejemplo completo
Rob Kennedy tuvo el buen punto de que no incluí el ejemplo de principio a fin.
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; } }
}
Lo ideal sería usar un ashx para enviar XML, aunque sí permití que el código en un ASPX interceptara la ejecución normal.
Response.Clear()
No uso esto si no estás seguro de que has descartado algo en la respuesta. Ve y encuéntralo y deshazte de él.
Response.ContentType = "text/xml"
Definitivamente, un cliente común no aceptará el contenido como XML sin este tipo de contenido presente.
Response.Charset = "UTF-8";
Deje que la clase de respuesta maneje la construcción del encabezado del tipo de contenido correctamente. Use UTF-8 a menos que tenga una razón realmente buena para no hacerlo.
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetAllowResponseInBrowserHistory(true);
Si no envía encabezados de caché, algunos navegadores (es decir, IE) almacenarán en caché la respuesta, las solicitudes posteriores no necesariamente llegarán al servidor. También necesita AllowResponseInBrowser si desea que esto funcione a través de HTTPS (debido a otro error en IE).
Para enviar contenido de un XmlDocument simplemente use:
dom.Save(Response.OutputStream);
dom.Save(Response.Output);
Solo asegúrese de que las codificaciones coincidan, (otra buena razón para usar UTF-8).
El objeto XmlDocument
ajustará automáticamente su encoding="..."
incrustada encoding="..."
a la de la Response
(por ejemplo, UTF-8
)
Response.End()
Si realmente tiene que hacerlo en un ASPX pero es un poco drástico, en un ASHX no lo haga.
Me sorprende que nadie haya mencionado nunca que se puede usar XDocument / XElement, que están disponibles en .NET 4.0 y que es mucho más fácil generar XML.
Parece que hay al menos 10 preguntas aquí, un par de puntos.
Response.Clear: realmente depende de qué más está sucediendo en la aplicación, si tiene httpmodules al principio de la canalización que podría estar escribiendo cosas que no desea, luego desactívelas. Pruébalo y averígualo. Fiddler o Wireshark útil para esto.
Tipo de contenido a texto / xml - yup - buena idea - lea en las especificaciones HTTP por qué esto es importante. La OMI que haga trabajo web debería haber leído las especificaciones 1.0 y 1.1 al menos una vez.
Codificación: cómo está codificado su xml, si es utf-8, dígalo, si no, diga algo más apropiado, solo asegúrese de que todos coincidan.
Página - personalmente, usaría ashx o httpmodule, si está usando la página, y la quiere un poco más rápido, deshágase de la función de autoeventwireup y vincule los manejadores de eventos manualmente.
Probablemente sería un desperdicio de memoria volcar primero el xml en una cadena, pero depende mucho del tamaño del xml en cuanto a si alguna vez lo notarías.
Como otros han sugerido, al guardar el xml en la secuencia de salida probablemente sea el más rápido, normalmente lo haría, pero si no está seguro, pruébelo, no confíe en lo que lee en el interweb. No solo creas todo lo que digo.
Para otro enfoque, si el xml no cambia mucho, puede simplemente escribirlo en el disco y servir el archivo directamente, lo que probablemente sea bastante eficiente, pero como todo en la programación, depende ...
A continuación se muestra el código del lado del servidor que llamaría al controlador y recibiría los datos de la ruta y se cargaría en xml doc.
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);
A continuación se muestra la forma en que un manejador devolverá los datos de flujo que contendrán datos xml en el lado del servidor.
Aquí está el código del manejador que devolvería los datos.
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();
}