zoukankan      html  css  js  c++  java
  • 如何实现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即可。

     

    小结

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

  • 相关阅读:
    Python学习笔记 for windows 二
    Socket.io:有点意思
    MEAN Stack:创建RESTful web service
    在EC2上安装MEAN环境
    NodeJS:树的序列化
    NodeJS:树的反序列化
    NodeJS学习:爬虫小探补完计划
    NodeJS学习:爬虫小探
    依赖包bcrypt安装Issues
    Mongoose:Schema之路
  • 原文地址:https://www.cnblogs.com/SvenLee86/p/5834236.html
Copyright © 2011-2022 走看看