2009-06-03 9 views
24

Por alguna extraña razón, deseo escribir HTML directamente en la secuencia de respuesta desde una acción del controlador. (entiendo la separación MVC, pero este es un caso especial.)Escribir en la secuencia de salida de la acción

Puedo escribir directamente en la corriente HttpResponse? En ese caso, ¿qué objeto IView debería devolver la acción del controlador? ¿Puedo devolver 'nulo'?

Respuesta

8

Sí, se puede escribir directamente a la respuesta. Cuando haya terminado, puede llamar a CompleteRequest() y no debería tener que devolver nada.

Por ejemplo:

// GET: /Test/Edit/5 
public ActionResult Edit(int id) 
{ 

    Response.Write("hi"); 
    HttpContext.ApplicationInstance.CompleteRequest(); 

    return View();  // does not execute! 
} 
+1

Debe evitar Response.End() http://stevesmithblog.com/blog/use-httpapplication-completerequest-instead-of-response-end/ –

+1

Actualizado entonces para utilizar CompleteRequest(). – womp

+1

Puede ser útil para reemplazar "return View()" por "return Content (" ")" para evitar errores en la vista que falta. Pero, ¿este enfoque es seguro? –

5

Escribe tu propia Acción Resultado. Aquí está un ejemplo de una de mina:

public class RssResult : ActionResult 
{ 
    public RssFeed RssFeed { get; set; } 

    public RssResult(RssFeed feed) { 
     RssFeed = feed; 
    } 

    public override void ExecuteResult(ControllerContext context) { 
     context.HttpContext.Response.ContentType = "application/rss+xml"; 
     SyndicationResourceSaveSettings settings = new SyndicationResourceSaveSettings(); 
     settings.CharacterEncoding = new UTF8Encoding(false); 
     RssFeed.Save(context.HttpContext.Response.OutputStream, settings); 
    } 
} 
43

utilicé una clase derivada de FileResult para lograr esto usando normal de patrón MVC:

/// <summary> 
/// MVC action result that generates the file content using a delegate that writes the content directly to the output stream. 
/// </summary> 
public class FileGeneratingResult : FileResult 
{ 
    /// <summary> 
    /// The delegate that will generate the file content. 
    /// </summary> 
    private readonly Action<System.IO.Stream> content; 

    private readonly bool bufferOutput; 

    /// <summary> 
    /// Initializes a new instance of the <see cref="FileGeneratingResult" /> class. 
    /// </summary> 
    /// <param name="fileName">Name of the file.</param> 
    /// <param name="contentType">Type of the content.</param> 
    /// <param name="content">Delegate with Stream parameter. This is the stream to which content should be written.</param> 
    /// <param name="bufferOutput">use output buffering. Set to false for large files to prevent OutOfMemoryException.</param> 
    public FileGeneratingResult(string fileName, string contentType, Action<System.IO.Stream> content,bool bufferOutput=true) 
     : base(contentType) 
    { 
     if (content == null) 
      throw new ArgumentNullException("content"); 

     this.content = content; 
     this.bufferOutput = bufferOutput; 
     FileDownloadName = fileName; 
    } 

    /// <summary> 
    /// Writes the file to the response. 
    /// </summary> 
    /// <param name="response">The response object.</param> 
    protected override void WriteFile(System.Web.HttpResponseBase response) 
    { 
     response.Buffer = bufferOutput; 
     content(response.OutputStream); 
    } 
} 

El método controlador ahora habría así:

public ActionResult Export(int id) 
{ 
    return new FileGeneratingResult(id + ".csv", "text/csv", 
     stream => this.GenerateExportFile(id, stream)); 
} 

public void GenerateExportFile(int id, Stream stream) 
{ 
    stream.Write(/**/); 
} 

Tenga en cuenta que si el búfer está desactivado,

stream.Write(/**/); 

se vuelve extremadamente lento. La solución es usar un BufferedStream. Al hacerlo, mejoró el rendimiento en aproximadamente 100x en un caso. Ver

Unbuffered Output Very Slow

+0

La mejor respuesta a esta pregunta. Buena idea. – pylover

+0

Otras respuestas son hacks – Andrey

+0

Mejor respuesta: simplemente agregue el archivo una vez y reutilice este concepto en cualquier otra situación utilizando el parámetro de delegado flexible. – Froyke

3

Si no desea derivar su propio tipo de resultado, sólo tiene que escribir en Response.OutputStream y volver new EmptyResult().

Cuestiones relacionadas