¿Cuál es la mejor forma de devolver XML desde la acción de un controlador en ASP.NET MVC? Hay una buena manera de devolver JSON, pero no para XML. ¿De verdad necesito enrutar el XML a través de una Vista, o debería hacer la mejor forma de Response.Write-ing?¿Devuelve XML de la acción de un controlador en ActionResult?
Respuesta
Utilice MVCContrib's XmlResult Action.
Como referencia aquí es su código:
public class XmlResult : ActionResult { private object objectToSerialize; /// <summary> /// Initializes a new instance of the <see cref="XmlResult"/> class. /// </summary> /// <param name="objectToSerialize">The object to serialize to XML.</param> public XmlResult(object objectToSerialize) { this.objectToSerialize = objectToSerialize; } /// <summary> /// Gets the object to be serialized to XML. /// </summary> public object ObjectToSerialize { get { return this.objectToSerialize; } } /// <summary> /// Serialises the object that was passed into the constructor to XML and writes the corresponding XML to the result stream. /// </summary> /// <param name="context">The controller context for the current request.</param> public override void ExecuteResult(ControllerContext context) { if (this.objectToSerialize != null) { context.HttpContext.Response.Clear(); var xs = new System.Xml.Serialization.XmlSerializer(this.objectToSerialize.GetType()); context.HttpContext.Response.ContentType = "text/xml"; xs.Serialize(context.HttpContext.Response.Output, this.objectToSerialize); } } }
Hay una XmlResult (y mucho más) en MVC Contrib. Echar un vistazo a http://www.codeplex.com/MVCContrib
Si usted está interesado sólo volver XML a través de una petición, y que tenga su xml "trozo", que sólo puede hacer (como una acción en el controlador):
public string Xml()
{
Response.ContentType = "text/xml";
return yourXmlChunk;
}
return this.Content(xmlString, "text/xml");
Guau, esto realmente me ayudó, pero luego estoy empezando a jugar con el MVC. –
Si está trabajando con Linq en XML, la creación de un formato de cadena del documento es un desperdicio: es [mejor trabajar con transmisiones] (http://stackoverflow.com/a/12718046/24874). –
@Drew Noakes: No, no lo es. Si escribe directamente en la secuencia HttpContext.Response.Output, obtendrá un YSOD en servidores basados en WinXP. Parece estar fijo en Vista +, lo que es especialmente problemático si desarrollas en Windows 7 e implementas en Windows XP (¿Servidor 2003?). Si lo hace, primero debe escribir en una secuencia de memoria y luego copiar la secuencia de la memoria en la secuencia de salida ... –
Si está creando el XML utilizando el excelente marco Linq-to-XML, entonces este enfoque será útil.
Creo un XDocument
en el método de acción.
public ActionResult MyXmlAction()
{
// Create your own XDocument according to your requirements
var xml = new XDocument(
new XElement("root",
new XAttribute("version", "2.0"),
new XElement("child", "Hello World!")));
return new XmlActionResult(xml);
}
Este reutilizable, a medida ActionResult
serialises el XML para usted.
public sealed class XmlActionResult : ActionResult
{
private readonly XDocument _document;
public Formatting Formatting { get; set; }
public string MimeType { get; set; }
public XmlActionResult(XDocument document)
{
if (document == null)
throw new ArgumentNullException("document");
_document = document;
// Default values
MimeType = "text/xml";
Formatting = Formatting.None;
}
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.Clear();
context.HttpContext.Response.ContentType = MimeType;
using (var writer = new XmlTextWriter(context.HttpContext.Response.OutputStream, Encoding.UTF8) { Formatting = Formatting })
_document.WriteTo(writer);
}
}
puede especificar un tipo MIME (como application/rss+xml
) y si la salida debe tener una sangría si es necesario. Ambas propiedades tienen valores predeterminados razonables.
Si necesita una codificación que no sea UTF8, también es fácil agregar una propiedad para eso.
¿Crees que es posible modificar esto para su uso en un controlador API? –
@RayAckley, no sé, ya que aún no he probado el nuevo API web. Si lo descubre, infórmenos. –
Creo que estaba en el camino equivocado con la pregunta del controlador API (normalmente no hago cosas MVC). Acabo de implementarlo como un controlador regular y funcionó muy bien. –
Finalmente logré obtener este trabajo y pensé que documentaría cómo hacerlo con la esperanza de salvar a otros el dolor.
Medio Ambiente
- VS2012
- SQL Server 2008R2
- .NET 4.5
- ASP.NET MVC4 (Razor)
- de Windows 7
Navegadores Web soportados
- FireFox 23
- IE 10
- Chrome 29
- Opera 16
- Safari 5.1.7 (la última para Windows?)
Mi tarea consistía en un clic del botón del ui, llamar un método en mi Controlador (con algunos parámetros) y luego hacer que devuelva un XML de MS-Excel mediante una transformación xslt. El XML MS-Excel devuelto haría que el navegador muestre el diálogo Abrir/Guardar. Esto tuvo que funcionar en todos los navegadores (mencionados anteriormente).
Al principio probé con Ajax y crear un ancla dinámica con el atributo "descargar" para el nombre de archivo, pero que solo funcionaba para 3 de los 5 navegadores (FF, Chrome, Opera) y no para IE o Safari. Y hubo problemas al intentar activar mediante programación el evento Click del anclaje para provocar la "descarga" real.
¡Lo que terminé haciendo era usar un IFRAME "invisible" y funcionó para los 5 navegadores!
Así que aquí es lo que ocurrió: [Tenga en cuenta que soy de ninguna manera un html/javascript gurú y sólo se han incluido en el código pertinente]
HTML (fragmento de bits relevantes)
<div id="docxOutput">
<iframe id="ifOffice" name="ifOffice" width="0" height="0"
hidden="hidden" seamless='seamless' frameBorder="0" scrolling="no"></iframe></div>
jAVASCRIPT
//url to call in the controller to get MS-Excel xml
var _lnkToControllerExcel = '@Url.Action("ExportToExcel", "Home")';
$("#btExportToExcel").on("click", function (event) {
event.preventDefault();
$("#ProgressDialog").show();//like an ajax loader gif
//grab the basket as xml
var keys = GetMyKeys();//returns delimited list of keys (for selected items from UI)
//potential problem - the querystring might be too long??
//2K in IE8
//4096 characters in ASP.Net
//parameter key names must match signature of Controller method
var qsParams = [
'keys=' + keys,
'locale=' + '@locale'
].join('&');
//The element with id="ifOffice"
var officeFrame = $("#ifOffice")[0];
//construct the url for the iframe
var srcUrl = _lnkToControllerExcel + '?' + qsParams;
try {
if (officeFrame != null) {
//Controller method can take up to 4 seconds to return
officeFrame.setAttribute("src", srcUrl);
}
else {
alert('ExportToExcel - failed to get reference to the office iframe!');
}
} catch (ex) {
var errMsg = "ExportToExcel Button Click Handler Error: ";
HandleException(ex, errMsg);
}
finally {
//Need a small 3 second (delay for the generated MS-Excel XML to come down from server)
setTimeout(function() {
//after the timeout then hide the loader graphic
$("#ProgressDialog").hide();
}, 3000);
//clean up
officeFrame = null;
srcUrl = null;
qsParams = null;
keys = null;
}
});
C# del lado del servidor (fragmento de código) @Drew creó un ActionResult personalizado llamado XmlActionResult que modifiqué para mi propósito.
Return XML from a controller's action in as an ActionResult?
Mi método Controller (devuelve ActionResult)
- pasa el parámetro de claves para un procedimiento almacenado SQL Server que genera un XML
- que XML se transforma entonces a través de XSLT en un MS- Excel xml (XmlDocument)
crea una instancia del XmlActionResult modificado y lo devuelve
XmlActionResult result = new XmlActionResult (excelXML, "application/vnd.ms-excel"); string version = DateTime.Now.ToString ("dd_MMM_aaaa_hhmmsstt"); string fileMask = "LabelExport_ {0} .xml";
result.DownloadFilename = string.Format (fileMask, version); resultado de devolución;
La principal modificación a la clase XmlActionResult que @Drew creado.
public override void ExecuteResult(ControllerContext context)
{
string lastModDate = DateTime.Now.ToString("R");
//Content-Disposition: attachment; filename="<file name.xml>"
// must set the Content-Disposition so that the web browser will pop the open/save dialog
string disposition = "attachment; " +
"filename=\"" + this.DownloadFilename + "\"; ";
context.HttpContext.Response.Clear();
context.HttpContext.Response.ClearContent();
context.HttpContext.Response.ClearHeaders();
context.HttpContext.Response.Cookies.Clear();
context.HttpContext.Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);// Stop Caching in IE
context.HttpContext.Response.Cache.SetNoStore();// Stop Caching in Firefox
context.HttpContext.Response.Cache.SetMaxAge(TimeSpan.Zero);
context.HttpContext.Response.CacheControl = "private";
context.HttpContext.Response.Cache.SetLastModified(DateTime.Now.ToUniversalTime());
context.HttpContext.Response.ContentType = this.MimeType;
context.HttpContext.Response.Charset = System.Text.UTF8Encoding.UTF8.WebName;
//context.HttpContext.Response.Headers.Add("name", "value");
context.HttpContext.Response.Headers.Add("Last-Modified", lastModDate);
context.HttpContext.Response.Headers.Add("Pragma", "no-cache"); // HTTP 1.0.
context.HttpContext.Response.Headers.Add("Expires", "0"); // Proxies.
context.HttpContext.Response.AppendHeader("Content-Disposition", disposition);
using (var writer = new XmlTextWriter(context.HttpContext.Response.OutputStream, this.Encoding)
{ Formatting = this.Formatting })
this.Document.WriteTo(writer);
}
Eso fue básicamente la misma. Espero que ayude a los demás.
Una opción simple que le permitirá usar transmisiones y todo lo que es return File(stream, "text/xml");
.
Aquí es una forma sencilla de hacerlo:
var xml = new XDocument(
new XElement("root",
new XAttribute("version", "2.0"),
new XElement("child", "Hello World!")));
MemoryStream ms = new MemoryStream();
xml.Save(ms);
return File(new MemoryStream(ms.ToArray()), "text/xml", "HelloWorld.xml");
que he tenido que hacer esto recientemente para un proyecto Sitecore que utiliza un método para crear un XmlDocument de un artículo Sitecore y sus hijos y lo devuelve desde el controlador ActionResult como un archivo. Mi solución:
public virtual ActionResult ReturnXml()
{
return File(Encoding.UTF8.GetBytes(GenerateXmlFeed().OuterXml), "text/xml");
}
Una pequeña variación de la answer from Drew Noakes que utilizan el método Save() de XDocument.
public sealed class XmlActionResult : ActionResult
{
private readonly XDocument _document;
public string MimeType { get; set; }
public XmlActionResult(XDocument document)
{
if (document == null)
throw new ArgumentNullException("document");
_document = document;
// Default values
MimeType = "text/xml";
}
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.Clear();
context.HttpContext.Response.ContentType = MimeType;
_document.Save(context.HttpContext.Response.OutputStream)
}
}
- 1. ¿Cómo se prueba un ActionResult que devuelve un ContentResult?
- 2. Devolver JsonResult utilizando ActionFilter en ActionResult en un controlador
- 3. ¿Por qué la mayoría del código del controlador de muestra MVC devuelve ActionResult?
- 4. backgroundworker bloqueando la acción del controlador MVC
- 5. Redirigir a la acción en otro controlador
- 6. Enviar MVC ActionResult a la impresora
- 7. Crear un enlace de acción en un controlador
- 8. ¿Cómo se prueba un método de acción que devuelve JsonResult?
- 9. ¿Filtros de acción MVC usando parámetros pasados a ActionResult?
- 10. Controlador MVC3 que devuelve JsonFile
- 11. controlador de Acción Prueba que utiliza User.Identity.Name
- 12. ¿Ejecuta la acción del controlador MVC sin la vista?
- 13. MVC3 Redirigir a la ruta desde ActionResult
- 14. Redirigir a la acción en otro controlador
- 15. Protección de la acción del controlador ASP.NET MVC que devuelve JSON
- 16. Capturar resultados HTML con un filtro de acción de controlador
- 17. Devuelve diferentes vistas en un controlador
- 18. Cómo devolver una cadena XML como un resultado de la acción en MVC
- 19. Rieles: nombre de la acción del controlador en la cadena
- 20. pase el modelo de una acción a otra acción en el mismo controlador
- 21. ASP .NET MVC: ¿Tiene un método de controlador que devuelve una imagen en la respuesta?
- 22. ASP.NET MVC: ¿Cómo convertir un ActionResult a una cadena?
- 23. ¿Cómo redirecciono al usuario a otra acción del controlador desde un filtro de acción ASP.MVC 3?
- 24. ASP.NET MVC3 Detener la ejecución de la acción/controlador en la costumbre AuthorizeAttribute
- 25. Devolución de una redirección 301 desde una acción de controlador
- 26. Render acción del controlador desde otro controlador
- 27. ASP.NET MVC Controller fileContent ActionResult llamada a través de AJAX
- 28. El uso de un aglutinante de modelo personalizado para un argumento de una acción de controlador
- 29. Cómo llegar controlador y nombre de la acción en ZF2
- 30. Pasando el parámetro a la acción del controlador desde Html.ActionLink
La clase aquí se toma directamente del proyecto MVC Contrib. No estoy seguro de si eso es lo que califica como rodar el suyo. –
¿Dónde ubicarías esta clase si sigues la convención ASP.NET MVC? Carpeta de controladores? ¿En el mismo lugar donde colocaría sus ViewModels, quizás? –
@pcampbel, prefiero crear carpetas separadas en mi raíz de proyecto para cada clase de clases: resultados, filtros, enrutamiento, etc. –