2016-03-07 8 views
0

Чтобы только отменить/отклонить ответы WebAPI, превышающие заданный предел, я должен узнать размер возвращаемого содержимого. Однако следующая строка:Замыкание при вызове LoadIntoBufferAsync в DelegatingHandler

response.Content.LoadIntoBufferAsync() 

, похоже, зашел в тупик позже в цикле ответа API. Размер определяется правильно, и обработчик выполняет штраф, но потом запрос ожидает навсегда.

Это метод SendAsync моего DelegatingHandler.

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
    { 
     return base.SendAsync(request, cancellationToken).ContinueWith(
      task => 
      { 
       HttpResponseMessage response = task.Result; 

       if (response.RequestMessage != null && response.RequestMessage.Headers.AcceptEncoding != null && response.RequestMessage.Headers.AcceptEncoding.Count > 0) 
       { 
        if (response.Content != null) 
        { 
         response.Content.LoadIntoBufferAsync(); // when I remove this line the request finishes, but then ContentLength = 0 

         if (response.Content.Headers.ContentLength > 4000) 
         { 
          string encodingType = GetEncodingType(response.RequestMessage.Headers.AcceptEncoding); // use deflate if possible 
          if (encodingType == "deflate" || encodingType == "gzip") 
          { 
           response.Content = new CompressedContent(response.Content, encodingType); 
          } 
         } 
        } 
       } 

       return response; 
      }, cancellationToken); 
    } 

Я пробовал разные комбинации с Wait (..), ConfigureAwait (..) и ContinueWith (..). С синтаксисом async/await у меня была та же проблема.

EDIT: SerializeToStreamAsync из компрессора (отвечает за сжатие потока контента):

protected override Task SerializeToStreamAsync(Stream stream, TransportContext context) 
    { 
     Stream compressedStream = null; 

     if (m_EncodingType == "gzip") 
     { 
      compressedStream = new GZipStream(stream, CompressionMode.Compress, true); 
     } 
     else if (m_EncodingType == "deflate") 
     { 
      compressedStream = new DeflateStream(stream, CompressionMode.Compress, true); 
     } 

     return m_OriginalContent.CopyToAsync(compressedStream).ContinueWith(task => 
     { 
      if (compressedStream != null) 
      { 
       compressedStream.Dispose(); 
      } 
     }); 
    } 

=> Apperently трубопровод нарушается, когда это вызывается после:

response.Content.LoadIntoBufferAsync() 

Вызов либо одно из два только работают, поэтому их проблема должна быть проблемой при чтении/записи потока контента.

+1

Ну, текущий вариант, безусловно, * неправильно *. Каждый раз, когда вы видите метод с именем, заканчивающимся 'Async', где возвращаемое значение игнорируется, оно должно вызывать сигналы тревоги. –

+0

Я думаю, проблема в том, что в моем классе CompressedContent я снова переопределяю содержимое с помощью zipped-потока и таким образом каким-то образом содержимое становится испорченным. – Philipp

ответ

0

Я пытался сделать то же самое. Похоже, что вы отсутствуете в следующей информации:

Ожидание ответа.Content.LoadIntoBufferAsync();

Я обнаружил, что вышеупомянутое не имеет ничего общего с тупиком. Проблема возникает, когда вы пытаетесь получить длину содержимого с помощью:

response.Content.Headers.ContentLength

, а затем попытаться получить доступ к заголовку, например:

private void AddHeaders() 
    { 
     foreach (var header in content.Headers) 
     { 
      Headers.TryAddWithoutValidation(header.Key, header.Value); 
     } 
     Headers.ContentEncoding.Add(compressor.EncodingType); 
    } 

Чтобы исправить это вам необходимо добавить заголовок selectivly, а не использовать цикл foreach. Например:

private void AddHeaders() 
    { 
     //foreach (var header in content.Headers) 
     //{ 
     // Headers.TryAddWithoutValidation(header.Key, header.Value); 
     //} 
     Headers.ContentType = new MediaTypeHeaderValue("application/json"); 
     Headers.ContentEncoding.Add(compressor.EncodingType); 
    } 

Другой вариант использования сжатия выборочно - использовать фильтры действий. Вы можете найти более подробную информацию об этом в следующем посте:

http://blog.developers.ba/asp-net-web-api-gzip-compression-actionfilter/

+0

Я обнаружил, что тупик приходит, когда вы используете: response.Content.Headers.ContentLength и затем пытаетесь получить доступ к заголовку, например: –