如何实现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 == "gzip" || x.Value == "deflate").ToList();

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

                actionExecutedContext.Request.Method != HttpMethod.Options)

            {

                var bytes = content.ReadAsByteArrayAsync().Result;

                if (acceptEncoding.FirstOrDefault().Value == "gzip")

                {

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

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

                }

                else if (acceptEncoding.FirstOrDefault().Value == "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 == 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即可。

 

小结

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

posted on 2016-09-02 17:22  SvenLee86  阅读(1513)  评论(1编辑  收藏  举报