2016-09-26 43 views
2

我已經實現了MultipartStreamProvider的自定義子類,以便將上傳的文件數據寫入自定義流。在寫入流之後,HttpContext有時會丟失。下面的代碼是一個簡化的repro來展示問題。如果自定義流實際上沒有在WriteAsync中執行任何異步工作(即,如果它保持在同一個線程中),那麼一切都按照您的預期工作。但是,只要我們將一些實際的異步工作引入到WriteAsync中(由Task.Delay在此模擬),那麼HttpContext就會(通常)丟失。我做錯了什麼,或者這是Web API框架中的錯誤?異步工作後MultipartStreamProvider失去HttpContext

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-- 
+0

你有(https://blogs.msdn.microsoft ['targetFramework'在'web.config'設置爲'4.5'或更高版本] .COM/Webdev的/ 2012/11/19 /全約-的httpRuntime-targetframework /)? –

+0

@StephenCleary感謝您的提示 - 事實證明它已設置爲4.0。但是,更改爲4.5似乎沒有幫助... – jr76

+0

@ jr75:您是否確實在ASP.NET 4.5或更新的版本上運行?你是否安裝了'Microsoft.Bcl.Async' NuGet軟件包? –

回答

3

原來,@ StephenCleary的建議是正確的:在web.config中設置targetFramework爲> = 4.5解決問題但是,您必須設置右側的 targetFramework屬性。我第一次嘗試

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

行更改爲

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

這一點也沒有影響。這是一個需要被更新的httpRuntime元素:

<httpRuntime targetFramework="4.5" />