2012-05-04 38 views
17

Tôi hiện đang làm việc để di chuyển một vài Bộ điều khiển MVC3 của tôi tới Bộ điều khiển Api MVC4. Tôi đã triển khai Cơ chế nén cho bộ điều khiển MVC3 Nhận phản hồi phương thức bằng cách nhập ActionFilterAttribute và ghi đè phương thức OnActionExecutiong. Sau khi một số nghiên cứu tôi thấy rằng tôi cần phải sử dụng ActionFilterMethod từ System.Web.HttpFilters. Sẽ rất tuyệt nếu ai đó có thể chia sẻ đoạn mã mẫu để tôi bắt đầu cho phản hồi HTTP nén này bằng GZipNén HTTP GET Response

+0

Tôi đang gặp vấn đề tương tự, mặc dù trong trường hợp của tôi, tôi đã bật tính năng nén IIS. Trong trường hợp của bạn, nó là nén IIS, hay bạn đã tạo trình xử lý tùy chỉnh? – Carvellis

+0

Có, tôi đã sử dụng trình xử lý tùy chỉnh cho điều này giống như cách mà Darin đã đề cập ở đây. –

Trả lời

39

Cách dễ nhất là enable compression trực tiếp ở cấp IIS.

Nếu bạn muốn làm điều đó ở cấp ứng dụng mà bạn có thể viết một tùy chỉnh ủy xử lý tin nhắn như trong 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(); 
      } 
     }); 
    } 
} 

Tất cả những gì còn lại bây giờ là đăng ký xử lý trong Application_Start:

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

Tôi nghĩ rằng có lỗi trong mã này (cũng như trong các ví dụ tương tự được tìm thấy trên web): Tiêu đề Nội dung Độ dài được đặt không chính xác vì Tiêu đề Nội dung Độ dài được sao chép từ nội dung được nén. Điều này có thể dễ dàng sao chép bằng cách truyền một StringContent thông qua Trình xử lý nén. Để khắc phục điều này, dòng có 'originalContent.Headers' cần được sửa như sau:' originalContent.Headers.Where (x => x.Key! = "Content-Length") ' –

+0

Mã sẽ thất bại nếu không có Accept-Encoding được cung cấp. 'if (response.RequestMessage.Headers.AcceptEncoding! = null)' nên là 'if (response.RequestMessage.Headers.AcceptEncoding.Any())' –

+0

Tôi khuyên bạn nên thêm những điều sau đây trong SendAsync giữa việc gán kiểu mã và gán of response.Content để cho phép các phản hồi lỗi trả về mà không cần nén 'if (response.StatusCode! = HttpStatusCode.OK || response.Content == null || string.IsNullOrWhiteSpace (encodingType)) trả về;' – Paul

6

Nếu bạn đang sử dụng IIS 7+, tôi sẽ nói nén IIS thành hỗ trợ nén GZIP. Chỉ turn it on.

Mặt khác, quá trình nén quá gần với kim loại cho bộ điều khiển. Bộ điều khiển lý tưởng nên hoạt động ở mức cao hơn nhiều so với byte và luồng.

+0

Nói chung tôi đồng ý, tuy nhiên mức độ nén IIS sẽ yêu cầu cấu hình của bất kỳ máy chủ nào sử dụng nó. – samosaris

3

Sử dụng một lớp và viết đoạn mã sau

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 
public class CompressFilter : ActionFilterAttribute 
{ 
    public override void OnActionExecuted(HttpActionExecutedContext context) 
    { 
     var acceptedEncoding = context.Response.RequestMessage.Headers.AcceptEncoding.First().Value; 
     if (!acceptedEncoding.Equals("gzip", StringComparison.InvariantCultureIgnoreCase) 
     && !acceptedEncoding.Equals("deflate", StringComparison.InvariantCultureIgnoreCase)) 
     { 
      return; 
     } 
     context.Response.Content = new CompressedContent(context.Response.Content, acceptedEncoding); 
    } 
} 

Bây giờ hãy tạo một lớp khác và viết mã sau đây.

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

Bây giờ sử dụng thuộc tính sau đây trong điều khiển hoặc trong bất kỳ phương pháp hành động api như thế này

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

Tôi có OWIN Middleware được cấu hình trên API Web của tôi và đây là giải pháp duy nhất có hiệu quả đối với tôi. Ngoài ra, bạn thực sự có thể nhắm mục tiêu những gì bạn muốn nén. Giải pháp tốt! – Elferone

Các vấn đề liên quan