[英]Write in ASP.NET response output stream from task continuation
I have a http handler which should write to output some text. 我有一个http处理程序,应该写入输出一些文本。 Text content is retrieved asynchronously, so I want to write to response stream in the ProcessRequest method like this: 文本内容是异步检索的,所以我想在ProcessRequest方法中写入响应流,如下所示:
GetContent().ContinueWith(task =>
{
using (var stream = task.Result)
{
stream.WriteTo(context.Response.OutputStream);
}
});
However, I get a NullReferenceException with the stack trace 但是,我得到一个带有堆栈跟踪的NullReferenceException
in System.Web.HttpWriter.BufferData(Byte[] data, Int32 offset, Int32 size, Boolean needToCopyData)
in System.Web.HttpWriter.WriteFromStream(Byte[] data, Int32 offset, Int32 size)
in System.Web.HttpResponseStream.Write(Byte[] buffer, Int32 offset, Int32 count)
in System.IO.MemoryStream.WriteTo(Stream stream)
in SomeHandler.<>c__DisplayClass1.<ProcessRequest>b__0(Task`1 task) in SomeHandler.cs:line 33
in System.Threading.Tasks.ContinuationTaskFromResultTask`1.InnerInvoke()
in System.Threading.Tasks.Task.Execute()
If I do not use ContinueWith and write response after task.Wait() - there are no errors, but, obviously it is not a solution. 如果我在task.Wait()之后不使用ContinueWith并写入响应 - 没有错误,但显然它不是解决方案。
var task = GetContent();
task.Wait();
using (var stream = task.Result)
{
stream.WriteTo(context.Response.OutputStream);
}
How can I eliminate this error? 如何消除此错误? (.net 4.0 is used) (.net 4.0使用)
You need to implement IHttpAsyncHandler
. 您需要实现IHttpAsyncHandler
。 Check "Walkthrough: Creating an Asynchronous HTTP Handler" . 检查“演练:创建异步HTTP处理程序” 。
On top of that, you can use async/await
to copy the stream (note CopyAsync
below). 最重要的是,您可以使用async/await
复制流(请注意下面的CopyAsync
)。 To be able to use async/await
and target .NET 4.0 with VS2012+, add Microsoft.Bcl.Async
package to your project. 为了能够使用async/await
并使用VS2012 +定位.NET 4.0,请将Microsoft.Bcl.Async
包添加到项目中。
This way, no threads are unnecessary blocked. 这样,没有线程被不必要阻止。 A complete example (untested): 一个完整的例子(未经测试):
public partial class AsyncHandler : IHttpAsyncHandler
{
async Task CopyAsync(HttpContext context)
{
using (var stream = await GetContentAsync(context))
{
await stream.CopyToAsync(context.Response.OutputStream);
}
}
#region IHttpAsyncHandler
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
return new AsyncResult(cb, extraData, CopyAsync(context));
}
public void EndProcessRequest(IAsyncResult result)
{
// at this point, the task has compeleted
// we use Wait() only to re-throw any errors
((AsyncResult)result).Task.Wait();
}
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
throw new NotImplementedException();
}
#endregion
#region AsyncResult
class AsyncResult : IAsyncResult
{
object _state;
Task _task;
bool _completedSynchronously;
public AsyncResult(AsyncCallback callback, object state, Task task)
{
_state = state;
_task = task;
_completedSynchronously = _task.IsCompleted;
_task.ContinueWith(t => callback(this), TaskContinuationOptions.ExecuteSynchronously);
}
public Task Task
{
get { return _task; }
}
#region IAsyncResult
public object AsyncState
{
get { return _state; }
}
public System.Threading.WaitHandle AsyncWaitHandle
{
get { return ((IAsyncResult)_task).AsyncWaitHandle; }
}
public bool CompletedSynchronously
{
get { return _completedSynchronously; }
}
public bool IsCompleted
{
get { return _task.IsCompleted; }
}
#endregion
}
#endregion
}
This problem could be based on the "context-switch" which happens when the task is executed in its own thread. 此问题可能基于“上下文切换”,该任务在其自己的线程中执行时发生。
The current HttpContext
is only available in the requests thread. 当前的HttpContext
仅在请求线程中可用。 You could however make a "local reference" to be able to access it (but that would not completely solve your problem - see below): 但是,你可以制作一个“本地参考”来访问它(但这不能完全解决你的问题 - 见下文):
var outputStream = context.Response.OutputStream;
GetContent().ContinueWith(task =>
{
using (var stream = task.Result)
{
stream.WriteTo(outputStream);
}
});
The problem now is, that your request is very likely already done when the ContinueWith
is executed so your stream to the client is already closed. 现在的问题是,当执行ContinueWith
时,您的请求很可能已经完成,因此您的客户端流已经关闭。
You need to write your content to the stream before this happens. 在此之前,您需要将内容写入流。
I need to strike the follwoing part of my answer. 我需要完成我的答案的下一部分。 Http handlers are not async
-capable in 4.0 as this neets the HttpTaskAsyncHandler
base-class which is not available before 4.5 => I don't know a way around the Wait
in the ProcessRequest
method in 4.0 when using a http handler: Http处理程序在4.0中不是async
可用的,因为它支持HttpTaskAsyncHandler
基类,这在4.5之前是不可用的=>当使用http处理程序时,我不知道4.0中ProcessRequest
方法中的Wait
的方法:
I recommend using something like the following:
我建议使用以下内容:
using(var result = await GetContent()) { stream.WriteTo(context.Response.OutputStream); }
and the appropriate
NuGet-Package for
await
.
以及适当的
NuGet-Package await
。
Quite recently, I had come across the similar task to write an ashx handler to put response asynchronously. 最近,我遇到了类似的任务,写了一个ashx处理程序来异步地放置响应。 The purpose was to generate some big string, perform I/O and return it back on the response stream, and thus benchmark ASP.NET with other languages like node.js for a heavily I/O bound app I'm going to develop. 目的是生成一些大字符串,执行I / O并将其返回到响应流上,从而使用其他语言(如node.js)对ASP.NET进行基准测试,以构建我将要开发的大量I / O绑定应用程序。 This is what I ended up doing (it works): 这就是我最终做的事情(它有效):
private StringBuilder payload = null;
private async void processAsync()
{
var r = new Random (DateTime.Now.Ticks.GetHashCode());
//generate a random string of 108kb
payload=new StringBuilder();
for (var i = 0; i < 54000; i++)
payload.Append( (char)(r.Next(65,90)));
//create a unique file
var fname = "";
do
{
//fname = @"c:\source\csharp\asyncdemo\" + r.Next (1, 99999999).ToString () + ".txt";
fname = r.Next (1, 99999999).ToString () + ".txt";
} while(File.Exists(fname));
//write the string to disk in async manner
using(FileStream fs = new FileStream(fname,FileMode.CreateNew,FileAccess.Write, FileShare.None,
bufferSize: 4096, useAsync: true))
{
var bytes=(new System.Text.ASCIIEncoding ()).GetBytes (payload.ToString());
await fs.WriteAsync (bytes,0,bytes.Length);
fs.Close ();
}
//read the string back from disk in async manner
payload = new StringBuilder ();
//FileStream ;
//payload.Append(await fs.ReadToEndAsync ());
using (var fs = new FileStream (fname, FileMode.Open, FileAccess.Read,
FileShare.Read, bufferSize: 4096, useAsync: true)) {
int numRead;
byte[] buffer = new byte[0x1000];
while ((numRead = await fs.ReadAsync (buffer, 0, buffer.Length)) != 0) {
payload.Append (Encoding.Unicode.GetString (buffer, 0, numRead));
}
}
//fs.Close ();
//File.Delete (fname); //remove the file
}
public void ProcessRequest (HttpContext context)
{
Task task = new Task(processAsync);
task.Start ();
task.Wait ();
//write the string back on the response stream
context.Response.ContentType = "text/plain";
context.Response.Write (payload.ToString());
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.