2012-05-04 25 views
17

Şu anda MVC3 Denetleyicilerimden birkaçını MVC4 Api Denetleyicilerine taşımaya çalışıyorum. MVC3 denetleyicisi için Sıkıştırma mekanizmasını uyguladım ActionFilterAttribute'u ve OnActionExecutiong yöntemini geçersiz kılarak Yöntem Yanıtlarını Alın. Bazı araştırmalardan sonra System.Web.HttpFilters'dan ActionFilterMethod kullanmam gerektiğini buldum. Birisi, GZipSıkıştır HTTP GET Yanıtı

+0

gibi Kontrolör veya herhangi API eylem yönteminde aşağıdaki özniteliğini kullanın. Durumunuzda, IIS sıkıştırması mıydı yoksa özel işleyici mi oluşturdunuz? – Carvellis

+0

Evet, bunun için Darin'in bahsettiği gibi, bunun için özel işleyici kullandım. –

cevap

39

En kolayı, enable compression doğrudan IIS düzeyinde. Uygulama düzeyinde yapmak istiyorsanız following post gösterildiği gibi

ileti işleyicisi devrederek özel bir yazabilirsiniz:

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(); 
      } 
     }); 
    } 
} 

Tüm bu Application_Start yılında işleyicisi kaydetmek için şimdi Geriye kalan tek:

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

Sanırım bu kodda bir hata var (web'de bulunan benzer örneklerin yanı sıra): Content-Length Header, Content-Length Üstbilgisi gziplenmiş içerikten kopyalandığından hatalı ayarlandı. Bu, Sıkıştırma İşleyicisi aracılığıyla bir StringContent geçirilerek kolayca çoğaltılabilir. Bunu düzeltmek için, 'originalContent.Headers' ile satırın şöyle düzeltilmesi gerekir:' originalContent.Headers.Where (x => x.Key! = "Content-Length") ' –

+0

Accept-Encoding yoksa kod başarısız olur. sağlanır. 'if (response.RequestMessage.Headers.AcceptEncoding! = null)' '(response.RequestMessage.Headers.AcceptEncoding.Any())' –

+0

ise olmalıdır. Aşağıdakileri kodlamaType ve atamaların ataması arasında SendAsync'e eklemenizi öneririm response.Concept ("response.StatusCode! = HttpStatusCode.OK || response.ContentCode! = HttpStatusCode.OK || response.Content == null || string.IsNullOrWhiteSpace (encodingType)) yanıtı olmadan dönmek için hata yanıtları sağlama özelliği." – Paul

6

kullanarak sıkıştırılan HTTP yanıtı için beni başlatmak için örnek kod parçasını paylaşabiliyorsa harika olur. IIS 7+ kullanıyorsanız, sıkıştırma işlemini GZIP sıkıştırmasını desteklediği için IIS'ye bırakın. Sadece turn it on. Diğer taraftan, sıkıştırma, kontrol cihazı için metale çok yakındır. İdeal olarak kontrolör, baytlardan ve akışlardan daha yüksek seviyede çalışmalıdır.

+0

Genel olarak kabul ediyorum, ancak IIS düzeyindeki sıkıştırma, onu kullanan herhangi bir sunucunun yapılandırılmasını gerektirir. – samosaris

3

bir sınıfını kullanın ve aşağıdaki kodu

yazma 210

Şimdi başka bir sınıf oluşturun ve aşağıdaki kodu yazın.

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(); 
      } 
     }); 
    } 
} 

Şimdi benim durumumda zaten IIS sıkıştırma özellikli rağmen, aynı sorunu yaşıyorum bu

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

ile biter. Web API'mda yapılandırılmış OWIN Middleware var ve bu benim için çalışan tek çözüm. Ayrıca, sıkıştırmak istediğiniz şeyi gerçekten hedefleyebilirsiniz. Güzel çözüm! – Elferone