首页 > 代码库 > 如何实现HttpClient + Web Api架构下数据的压缩

如何实现HttpClient + Web Api架构下数据的压缩

随着移动互联网的发展,各种终端设备的流行,服务器接口要求统一,这个时候RESTful就有用武之地。RESTful一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。ASP.NET WebAPI,其核心概念就是构建REST风格的Web服务,把一起数据视为资源,无论是服务请求或者是数据操作。

 

HttpClient + Web Api实现Restful服务

 

下面实现了提交一个对象json数据到服务器上请求创建操作,对应Http的POST请求。

1)  准备需要传递给WebAPI的参数

2)  然后用System.Net.Http.StringContent把它打个包

3)  然后设置一下ContentType

4)  通过Http Post调用WebAPI得到返回结果

5)  最后将Json格式返回的结果反序列化为强类型

 

public ErrorCode Save(){    var requestJson = JsonConvert.SerializeObject(new {name = "apple", number = 10});    HttpContent httpContent = new StringContent(requestJson);    httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");    HttpClient httpClient = new HttpClient();    var respJsonJson = httpClient.PostAsync("http://localhost:9000/api/order/save", httpContent).Result.Content.ReadAsStringAsync().Result;    var result = JsonConvert.DeserializeObject<ErrorCode>(respJsonJson);    return result;}

 

本文基于Http Client+ASP.NET Web Api实现Restful服务访问和提供, 主要介绍如何实现数据量大的情况下进行压缩处理。

 

ASP.NET Web APi 压缩

对于减少响应包的大小和提高响应速度,压缩是一种简单而有效的方式。那么如何实现对ASP.NET Web API 进行压缩呢, System.IO.Compression中提供了对应的类库——GZipStream与DeflateStream,我们只需要在HttpClient与Web API中应用它们即可。

Code View

 

Web Api  code

这里我们使用特性来完成Web API端数据的压缩。

public class CompressAttribute : ActionFilterAttribute

    {

        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)

        {

            var content = actionExecutedContext.Response.Content;

            var acceptEncoding = actionExecutedContext.Request.Headers.AcceptEncoding.

                Where(x => x.Value =http://www.mamicode.com/="gzip" || x.Value =http://www.mamicode.com/="deflate").ToList();

            if (acceptEncoding != null && acceptEncoding.Count > 0 && content != null &&

                actionExecutedContext.Request.Method != HttpMethod.Options)

            {

                var bytes = content.ReadAsByteArrayAsync().Result;

                if (acceptEncoding.FirstOrDefault().Value =http://www.mamicode.com/="gzip")

                {

                    actionExecutedContext.Response.Content = new ByteArrayContent(CompressionHelper.GzipCompress(bytes));

actionExecutedContext.Response.Content.Headers.Add("Content-Encoding", "gzip");

                }

                else if (acceptEncoding.FirstOrDefault().Value =http://www.mamicode.com/="deflate")

                {

                    actionExecutedContext.Response.Content = new ByteArrayContent(CompressionHelper.DeflateCompress(bytes));

actionExecutedContext.Response.Content.Headers.Add("Content-encoding", "deflate");

                }

            }

            base.OnActionExecuted(actionExecutedContext);

        }

    }

 

 

 

public static byte[] DeflateCompress(byte[] data)

        {

            if (data =http://www.mamicode.com/= null || data.Length < 1)

                return data;

            try

            {

                using (MemoryStream stream = new MemoryStream())

                {

                    using (DeflateStream gZipStream = new DeflateStream(stream, CompressionMode.Compress))

                    {

                        gZipStream.Write(data, 0, data.Length);

                        gZipStream.Close();

                    }

                    return stream.ToArray();

                }

            }

            catch (Exception)

            {

                return data;

            }

        }

 

当然如果使用GZIP压缩的话,只需要将DeflateStream改成GZipStream

 

 

HttpClient  Code

对于响应内容的压缩,一般Web服务器(比如IIS)都提供了内置支持,只需在请求头中包含 Accept-Encoding: gzip, deflate ,客户端浏览器与HttpClient都提供了内置的解压支持。

HttpClient httpClient = new HttpClient(new HttpClientHandler

{

AutomaticDecompression =System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate

 });

这里首先判断客户端是否启动gzip,deflate压缩,如果启用并且请求类型不是Options同时又返回数据,那么我们就压缩返回数据。

 

 

HTTP Client压缩

我们用HttpClient调用第三方Web API或者iOS App调用自己的Web API时需要提交大文本数据的时候也需要对请求的数据进行压缩。

对于请求内容的压缩,.NET中的HttpClient并没有提供内置支持,IIS也没有提供对解压的内置支持,需要自己写代码实现。同上继续使用类库——GZipStream与DeflateStream。

 

Code View

HttpClient code

我们需要实现一个支持压缩的HttpContent——CompressedContent

 

public class CompressedContent : HttpContent    {        private readonly HttpContent _originalContent;        private readonly CompressionMethod _compressionMethod;        public CompressedContent(HttpContent content, CompressionMethod compressionMethod)        {            if (content == null)            {                throw new ArgumentNullException("content");            }            _originalContent = content;            _compressionMethod = compressionMethod;            foreach (KeyValuePair<string, IEnumerable<string>> header in _originalContent.Headers)            {                Headers.TryAddWithoutValidation(header.Key, header.Value);            }Headers.ContentEncoding.Add(_compressionMethod.ToString().ToLowerInvariant());        }         protected override bool TryComputeLength(out long length)        {            length = -1;            return false;        }         protected async override Task SerializeToStreamAsync(Stream stream, TransportContext context)        {            if (_compressionMethod == CompressionMethod.GZip)            {                using (var gzipStream = new GZipStream(stream, CompressionMode.Compress, leaveOpen: true))                {                    await _originalContent.CopyToAsync(gzipStream);                }            }            else if (_compressionMethod == CompressionMethod.Deflate)            {                using (var deflateStream = new DeflateStream(stream, CompressionMode.Compress, leaveOpen: true))                {                    await _originalContent.CopyToAsync(deflateStream);                }            }        }    }

 

 

主要就是重载HttpContent.SerializeToStreamAsync()方法,在其中使用相应的压缩算法进行压缩。HttpClient使用这个CompressedContent的方法如下:

var httpContent = new CompressedContent(new StringContent(requestjson, Encoding.UTF8, "application/json"), CompressionMethod.GZip);HttpResponseMessage response = httpClient.PostAsync(url, httpContent).Result;

 

Web Api  code

这里我们需要实现一个DelegatingHandler——DecompressionHandler:

 

public class DecompressionHandler : DelegatingHandler    {        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)        {            if (request.Method == HttpMethod.Post || request.Method == HttpMethod.Put)            {                bool isGzip = request.Content.Headers.ContentEncoding.Contains("gzip");                bool isDeflate = !isGzip && request.Content.Headers.ContentEncoding.Contains("deflate");                if (isGzip || isDeflate)                {                    Stream decompressedStream = new MemoryStream();                    if (isGzip)                    {                        using (var gzipStream = new GZipStream(await request.Content.ReadAsStreamAsync(),CompressionMode.Decompress))                        {                            await gzipStream.CopyToAsync(decompressedStream);                        }                    }                    else                    {                        using (var gzipStream = new DeflateStream(await request.Content.ReadAsStreamAsync(),CompressionMode.Decompress))                        {                            await gzipStream.CopyToAsync(decompressedStream);                        }                    }                    decompressedStream.Seek(0, SeekOrigin.Begin);                    var originContent = request.Content;                    request.Content = new StreamContent(decompressedStream);                    foreach (var header in originContent.Headers)                    {                        request.Content.Headers.Add(header.Key, header.Value);                    }                }            }            return await base.SendAsync(request, cancellationToken);        }    }

 

主要重载DelegatingHandler.SendAsync()方法,用GZipStream或DeflateStream完成解压操作。然后在WebApiConfig中应用这个DecompressionHandler即可。

 

小结

最后大家要注意一下数据压缩的临界区域。当被压缩的数据超过一定大小后,我们压缩才能提升性能,反之则起反作用,这个需要项目开发过程中去不断尝试来确定。同时我们可以使用抓包工具来抓取一下压缩前后的数据大小,以验证我们压缩的实际效果。

如何实现HttpClient + Web Api架构下数据的压缩