【发布时间】:2017-02-03 23:47:51
【问题描述】:
我已经实现了 MultipartStreamProvider 的自定义子类,以便将上传的文件数据写入自定义流。写入流后,HttpContext 有时会丢失。下面的代码是演示问题的简化重现。如果自定义流实际上没有在 WriteAsync 中执行任何异步工作(即,如果它保持在同一个线程上),那么一切都会按您的预期工作。但是,一旦我们将一些实际的异步工作引入到 WriteAsync(这里由 Task.Delay 模拟),那么 HttpContext(通常)就会丢失。是我做错了什么,还是 Web API 框架中的错误?
public class TestApiController : ApiController
{
public class CustomMultipartStreamProvider : MultipartStreamProvider
{
private readonly List<string> _fileNames = new List<string>();
private readonly List<Stream> _fileStreams = new List<Stream>();
public CustomMultipartStreamProvider()
{
}
public override Stream GetStream(HttpContent parent, HttpContentHeaders headers)
{
string filename;
if (IsFileContent(headers, out filename))
{
var stream = new CustomStream();
_fileStreams.Add(stream);
_fileNames.Add(filename);
return stream;
}
return new MemoryStream();
}
private static bool IsFileContent(HttpContentHeaders headers, out string filename)
{
var contentDisposition = headers.ContentDisposition;
if (contentDisposition == null)
{
filename = null;
return false;
}
filename = UnquoteToken(contentDisposition.FileName);
return !string.IsNullOrEmpty(filename);
}
private static string UnquoteToken(string token)
{
if (string.IsNullOrWhiteSpace(token))
return token;
if (token.StartsWith("\"", StringComparison.Ordinal) && token.EndsWith("\"", StringComparison.Ordinal) && token.Length > 1)
return token.Substring(1, token.Length - 2);
return token;
}
}
class CustomStream : MemoryStream
{
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
await Task.Delay(100, cancellationToken); // simulate async work (if this line is commented out, everything works correctly)
await base.WriteAsync(buffer, offset, count, cancellationToken);
}
}
[Route("api/test/multipart")]
public async Task<string> PutMultiPart()
{
// Check if the request contains multipart/mixed content
if (!Request.Content.IsMimeMultipartContent("mixed"))
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
// Read the multipart data
var multipartStreamProvider = new CustomMultipartStreamProvider();
await Request.Content.ReadAsMultipartAsync(multipartStreamProvider);
if (HttpContext.Current != null)
{
return "good";
}
else
{
return "bad";
}
}
}
出于测试目的,我发送的请求本质上是这样的:
Content-Type: multipart/mixed; boundary=boundary42
--boundary42
Content-Type: application/json
{
Description: "test file"
}
--boundary42
Content-Type: application/octet-stream
Content-Disposition: inline; filename=hello.txt
Hello world.
--boundary42--
【问题讨论】:
-
@StephenCleary 感谢您的提示-原来它已设置为 4.0。但是,更改为 4.5 似乎没有帮助...
-
@jr75:您实际上是在 ASP.NET 4.5 或更高版本上运行吗?您是否安装了
Microsoft.Bcl.AsyncNuGet 包? -
@StephenCleary 我很确定我们正在运行 ASP.NET 4.6。该项目的目标是 .NET 4.6.1,我们没有使用 Bcl.Async,并且 async/await 似乎确实在 ASP.NET 中对我们普遍有效——这只是我遇到问题的一种情况。
标签: asp.net asp.net-web-api async-await asp.net-web-api2