MVC 中对返回的 data 进行压缩

在webAPI 中返回数据,在数据量比较大的情况的下,返回的data 也可能比较大,有时候可能大于1兆,因此对数据进行压缩能极大的提高数据下载到客户端的时间,提高页面的加载速度。

思路: 在web api 中添加 action filter attribute 来实现,我们先看下其定义:

 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
  public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IFilter
  {
    /// <summary>
    /// Occurs before the action method is invoked.
    /// </summary>
    /// <param name="actionContext">The action context.</param>
    public virtual void OnActionExecuting(HttpActionContext actionContext);
    /// <summary>
    /// Occurs after the action method is invoked.
    /// </summary>
    /// <param name="actionExecutedContext">The action executed context.</param>
    public virtual void OnActionExecuted(HttpActionExecutedContext actionExecutedContext);
    /// <summary>
    /// Executes the filter action asynchronously.
    /// </summary>
    /// 
    /// <returns>
    /// The newly created task for this operation.
    /// </returns>
    /// <param name="actionContext">The action context.</param><param name="cancellationToken">The cancellation token assigned for this task.</param><param name="continuation">The delegate function to continue after the action method is invoked.</param>
    Task<HttpResponseMessage> IActionFilter.ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation);
  }

代码很清晰,关键是两个虚方法 OnActionExecuting(在controller 调用 action方法之前执行),OnActionExecuted( 在controller 调用 action方法之后执行)。

因为我们要对action 执行后的json 进行压缩,所以我们只需要重写OnActionExecuted 方法即可。

在工程中添加一个类CompressResponseAttribute.cs,

具体实现代码如下:

    public class CompressResponseAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            var request = actionExecutedContext.Request;
            var response = actionExecutedContext.Response;

            var contentBytes = response.Content.ReadAsByteArrayAsync().Result;
            byte[] compressedBytes;
            string contentEncoding = string.Empty;

            using (var output = new MemoryStream())
            {
                if (request.Headers.AcceptEncoding.Any(v => v.Value.Equals("gzip", StringComparison.OrdinalIgnoreCase)))
                {
                    contentEncoding = "gzip";
                    using (var gZipStream = new GZipStream(output, CompressionMode.Compress))
                    {
                        gZipStream.Write(contentBytes, 0, contentBytes.Length);
                    }
                }
                else if (request.Headers.AcceptEncoding.Any(v => v.Value.Equals("deflate", StringComparison.OrdinalIgnoreCase)))
                {
                    contentEncoding = "deflate";
                    using (var deflateStream = new DeflateStream(output, CompressionMode.Compress))
                    {
                        deflateStream.Write(contentBytes, 0, contentBytes.Length);
                    }
                }
                compressedBytes = output.ToArray();
            }
                    

            if (!string.IsNullOrEmpty(contentEncoding))
            {
                var originalContentType = response.Content.Headers.ContentType.ToString();

                response.Content = new System.Net.Http.ByteArrayContent(compressedBytes);
                response.Content.Headers.Remove("Content-Type");
                response.Content.Headers.Add("Content-encoding", contentEncoding);
                response.Content.Headers.Add("Content-Type", originalContentType);
            }

            base.OnActionExecuted(actionExecutedContext);
        }
    }

上面的代码实现的压缩方式优先为gzip。

最后,在需要压缩返回的method 上面添加 [CompressResponse] 标注  即可。

 

posted @ 2015-01-05 13:17  大鱼人  阅读(540)  评论(0编辑  收藏  举报