2008-09-25 15 views

Respuesta

105

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); 
     } 
    } 
} 
+11

La clase aquí se toma directamente del proyecto MVC Contrib. No estoy seguro de si eso es lo que califica como rodar el suyo. –

+3

¿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? –

+7

@pcampbel, prefiero crear carpetas separadas en mi raíz de proyecto para cada clase de clases: resultados, filtros, enrutamiento, etc. –

23

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; 
} 
108
return this.Content(xmlString, "text/xml"); 
+1

Guau, esto realmente me ayudó, pero luego estoy empezando a jugar con el MVC. –

+0

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). –

+2

@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 ... –

30

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.

+0

¿Crees que es posible modificar esto para su uso en un controlador API? –

+0

@RayAckley, no sé, ya que aún no he probado el nuevo API web. Si lo descubre, infórmenos. –

+0

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

2

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.

1

Una opción simple que le permitirá usar transmisiones y todo lo que es return File(stream, "text/xml");.

0

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

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"); 
} 
0

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) 
    } 
} 
Cuestiones relacionadas