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] 标注 即可。