【发布时间】:2010-10-30 21:32:10
【问题描述】:
出于一些奇怪的原因,我想将 HTML 从控制器操作直接写入响应流。 (我理解MVC分离,但这是特例。)
我可以直接写入HttpResponse 流吗?在这种情况下,控制器操作应该返回哪个IView 对象?我可以返回“null”吗?
【问题讨论】:
标签: asp.net-mvc
出于一些奇怪的原因,我想将 HTML 从控制器操作直接写入响应流。 (我理解MVC分离,但这是特例。)
我可以直接写入HttpResponse 流吗?在这种情况下,控制器操作应该返回哪个IView 对象?我可以返回“null”吗?
【问题讨论】:
标签: asp.net-mvc
您可以使用return Content(...);,如果我没记错的话,... 将是您想要直接写入输出流的内容,或者什么都不写。
看看Controller上的Content方法:http://aspnet.codeplex.com/SourceControl/changeset/view/22907#266451
还有ContentResult:http://aspnet.codeplex.com/SourceControl/changeset/view/22907#266450
【讨论】:
是的,您可以直接写入响应。完成后,您可以调用 CompleteRequest() 并且您不需要返回任何内容。
例如:
// GET: /Test/Edit/5
public ActionResult Edit(int id)
{
Response.Write("hi");
HttpContext.ApplicationInstance.CompleteRequest();
return View(); // does not execute!
}
【讨论】:
编写您自己的操作结果。这是我的一个例子:
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);
}
}
【讨论】:
我使用了一个派生自FileResult 的类来使用普通的 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);
}
}
控制器方法现在是这样的:
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(/**/);
}
注意,如果关闭缓冲,
stream.Write(/**/);
变得非常缓慢。解决方案是使用 BufferedStream。这样做在一种情况下将性能提高了大约 100 倍。见
【讨论】:
OutOfMemoryException。您可以通过关闭缓冲来解决。像这样向WriteFile() 添加一行:response.Buffer = false;
如果您不想派生自己的结果类型,您可以简单地写信给Response.OutputStream 并返回new EmptyResult()。
【讨论】: