For some strange reasons, I want to write HTML directly to the Response stream from a controller action. (I understand MVC separation, but this is a special case.)
C
If you don't want to derive your own result type, you can simply write to Response.OutputStream
and return new EmptyResult()
.
You can do return Content(...);
where, if I remember correctly, ...
would be what you want to write directly to the output stream, or nothing at all.
Take a look at the Content
methods on the Controller
: http://aspnet.codeplex.com/SourceControl/changeset/view/22907#266451
And the ContentResult
: http://aspnet.codeplex.com/SourceControl/changeset/view/22907#266450
Write your own Action Result. Here's an example of one of mine:
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);
}
}
I used a class derived from FileResult
to achieve this using normal MVC pattern:
/// <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);
}
}
The controller method would now be like this:
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(/**/);
}
Note that if buffering is turned off,
stream.Write(/**/);
becomes extremely slow. The solution is to use a BufferedStream. Doing so improved performance by approximately 100x in one case. See
Unbuffered Output Very Slow
Yes, you can write directly to the Response. After you're done, you can call CompleteRequest() and you shouldn't need to return anything.
For example:
// GET: /Test/Edit/5
public ActionResult Edit(int id)
{
Response.Write("hi");
HttpContext.ApplicationInstance.CompleteRequest();
return View(); // does not execute!
}