2012-05-04 23 views
17

現在、MVC3コントローラをMVC4コントローラに移行する作業はほとんどありません。 MVC3コントローラ用の圧縮メカニズムを実装しました。ActionFilterAttributeを継承し、OnActionExecutiongメソッドを上書きしてメソッド応答を取得しました。いくつかの研究の後、ActionFilterMethodSystem.Web.HttpFiltersから使用する必要があることがわかりました。誰かがサンプルコードを共有してGZipを使用してHTTPレスポンスを圧縮することができたら嬉しいですHTTP GETレスポンスを圧縮

+0

のようなコントローラまたは任意のAPIのアクションメソッドでは、次の属性を使用します。あなたの場合は、それはIISの圧縮ですか、またはカスタムハンドラを作成しましたか? – Carvellis

+0

はい、ダリンがここで言及したのとまったく同じ方法でカスタムハンドラを使用しました。 –

答えて

39

最も簡単なのは、IISレベルで直接enable compressionになります。

あなたはアプリケーションレベルでそれをしたい場合はfollowing postに示すように、メッセージハンドラを委任するカスタムを書くことができ:

public class CompressHandler : DelegatingHandler 
{ 
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 
    { 
     return base.SendAsync(request, cancellationToken).ContinueWith<HttpResponseMessage>((responseToCompleteTask) => 
     { 
      HttpResponseMessage response = responseToCompleteTask.Result; 

      if (response.RequestMessage.Headers.AcceptEncoding != null) 
      { 
       string encodingType = response.RequestMessage.Headers.AcceptEncoding.First().Value; 

       response.Content = new CompressedContent(response.Content, encodingType); 
      } 

      return response; 
     }, 
     TaskContinuationOptions.OnlyOnRanToCompletion); 
    } 
} 

public class CompressedContent : HttpContent 
{ 
    private HttpContent originalContent; 
    private string encodingType; 

    public CompressedContent(HttpContent content, string encodingType) 
    { 
     if (content == null) 
     { 
      throw new ArgumentNullException("content"); 
     } 

     if (encodingType == null) 
     { 
      throw new ArgumentNullException("encodingType"); 
     } 

     originalContent = content; 
     this.encodingType = encodingType.ToLowerInvariant(); 

     if (this.encodingType != "gzip" && this.encodingType != "deflate") 
     { 
      throw new InvalidOperationException(string.Format("Encoding '{0}' is not supported. Only supports gzip or deflate encoding.", this.encodingType)); 
     } 

     // copy the headers from the original content 
     foreach (KeyValuePair<string, IEnumerable<string>> header in originalContent.Headers) 
     { 
      this.Headers.AddWithoutValidation(header.Key, header.Value); 
     } 

     this.Headers.ContentEncoding.Add(encodingType); 
    } 

    protected override bool TryComputeLength(out long length) 
    { 
     length = -1; 

     return false; 
    } 

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

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

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

すべてのことが、今残っているがApplication_Startでハンドラを登録することです:

GlobalConfiguration.Configuration.MessageHandlers.Add(new CompressHandler()); 
+0

私は、このコードにバグがあると思います(ウェブ上にある同様の例と同様です):gzipされたコンテンツからContent-Lengthヘッダーがコピーされるため、Content-Lengthヘッダーが正しく設定されていません。これは、圧縮ハンドラを介してStringContentを渡すことで簡単に再現できます。 'originalContent.Headers.Where(x => x.Key!=" Content-Length ")' –

+0

Accept-Encodingがなければコードは失敗します供給される。 –

+0

私はSendAsyncで次をencoding_typeの代入と代入の間に追加することをお勧めします。(もし、if(response.RequestMessage.Headers.AcceptEncoding!= null) 'がif(response.RequestMessage.Headers.AcceptEncoding.Any()エラーレスポンスを圧縮せずに返すようにするためのコンテンツ 'if(response.StatusCode!= HttpStatusCode.OK || response.Content == null || string.IsNullOrWhiteSpace(encodingType))レスポンスを返す; – Paul

6

IIS 7+を使用している場合は、GZIP圧縮をサポートしているのでIISに圧縮を残しておきます。ただturn it on

一方、圧縮はコントローラの金属に近すぎます。理想的にはコントローラはバイトとストリームよりもはるかに高いレベルで動作するはずです。

+0

一般に私は同意しますが、IISレベルの圧縮では、それを使用するサーバーの構成が必要になります。 – samosaris

3

クラスを使用して、次のコード

を書きます210

別のクラスを作成し、次のコードを記述します。

public class CompressedContent : HttpContent 
{ 
    private readonly string _encodingType; 
    private readonly HttpContent _originalContent; 
    public CompressedContent(HttpContent content, string encodingType = "gzip") 
    { 
     if (content == null) 
     { 
      throw new ArgumentNullException("content"); 
     } 
     _originalContent = content; 
     _encodingType = encodingType.ToLowerInvariant(); 
     foreach (var header in _originalContent.Headers) 
     { 
      Headers.TryAddWithoutValidation(header.Key, header.Value); 
     } 
     Headers.ContentEncoding.Add(encodingType); 
    } 
    protected override bool TryComputeLength(out long length) 
    { 
     length = -1; 
     return false; 
    } 
    protected override Task SerializeToStreamAsync(Stream stream, TransportContext context) 
    { 
     Stream compressedStream = null; 
     switch (_encodingType) 
     { 
      case "gzip": 
       compressedStream = new GZipStream(stream, CompressionMode.Compress, true); 
       break; 
      case "deflate": 
       compressedStream = new DeflateStream(stream, CompressionMode.Compress, true); 
       break; 
      default: 
       compressedStream = stream; 
       break; 
     } 
     return _originalContent.CopyToAsync(compressedStream).ContinueWith(tsk => 
     { 
      if (compressedStream != null) 
      { 
       compressedStream.Dispose(); 
      } 
     }); 
    } 
} 

今、私の場合、私はすでにIISの圧縮を有効にするが、私は、同じ問題を抱えている。この

[Route("GetData")] 
[CompressFilter]   
public HttpResponseMessage GetData() 
{ 
} 
+0

Web APIにOWIN Middlewareが設定されています。さらに、圧縮したいものを実際にターゲットにすることもできます。良い解決策! – Elferone

関連する問題