在异步工作后,MultipartStreamProvider丢失HttpContext

时间:2016-09-26 14:58:44

标签: asp.net asp.net-web-api async-await asp.net-web-api2

我已经实现了MultipartStreamProvider的自定义子类,以便将上传的文件数据写入自定义流。写入流后,HttpContext有时会丢失。下面的代码是一个简化的repro,演示了这个问题。如果自定义流实际上没有在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--

1 个答案:

答案 0 :(得分:3)

事实证明@ StephenCleary的建议是正确的:将web.config中的targetFramework设置为&gt; = 4.5解决了问题,但是,您必须设置正确 targetFramework属性。我首先尝试更改行

<compilation debug="true" targetFramework="4.0" />

<compilation debug="true" targetFramework="4.5" />

这没有效果。这是需要更新的httpRuntime元素:

<httpRuntime targetFramework="4.5" />