首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将请求从角压缩到web API

将请求从角压缩到web API
EN

Stack Overflow用户
提问于 2015-12-13 13:54:12
回答 1查看 7K关注 0票数 13

我试图通过压缩从我的角度客户端到一个AspNet Web的请求来优化带宽的使用。有办法做到这一点吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-12-13 14:51:24

一种可能是使用行业标准算法来压缩数据,如gzip。它们为原始字符串提供了非常好的压缩,如果您要向服务器发送大型对象,那么可以通过减少请求的大小来获得性能。更不用说当你的应用程序在带宽有限的移动设备上运行时所获得的好处了。

但不要再喋喋不休了,我们开始练习吧。这里最大的挑战是在javascript中生成有效的gzip请求。一种可能的方法是阅读这种格式的规范并滚动您自己的实现,或者使用一些现有的库。其中一个我觉得特别有趣的是pako

使用bower在应用程序中安装非常简单,只需发出以下命令:

代码语言:javascript
复制
bower install pako

现在,让我们从客户端的角度来看看样例请求的外观。假设您希望向服务器发送以下JSON (作为POST或PUT谓词):

代码语言:javascript
复制
{ my: 'super', puper: [456, 567], awesome: 'pako' }

您可以简单地使用现代浏览器中可用的普通XMLHttpRequest对象(如果您对角度特定的解决方案感兴趣,请阅读下面的内容):

代码语言:javascript
复制
<script src="bower_components/pako/dist/pako.min.js"></script>
<script>
    var xhr = new XMLHttpRequest();
    xhr.open('POST', '/api/myresource', true);

    // Indicate to the serve that you will be sending data in JSON format
    xhr.setRequestHeader('Content-Type', 'application/json');
    // Indicate to the server that your data is encoded using the gzip format
    xhr.setRequestHeader('Content-Encoding', 'gzip');

    xhr.onreadystatechange = function (e) {
        if (this.readyState == 4 && this.status == 200) {
            alert('We have just successfully sent a gzip encoded payload to the server');
        }
    };

    var data = { my: 'super', puper: [456, 567], awesome: 'pako' };
    var binaryString = pako.gzip(JSON.stringify(data));
    xhr.send(binaryString);
</script>

既然您询问了一个角度请求,那么让我们使用本机$http对象对这个示例AJAX请求进行Angularify处理:

代码语言:javascript
复制
<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta charset="utf-8" />
</head>
<body ng-app="myapp">
    <div ng-controller="HomeController"></div>
    <script src="bower_components/pako/dist/pako.min.js"></script>
    <script src="bower_components/angular/angular.min.js"></script>
    <script>
        angular.module('myapp', []).controller('HomeController', ['$http', function ($http) {
            var data = { my: 'super', puper: [456, 567], awesome: 'pako' };
            var binaryString = pako.gzip(JSON.stringify(data));
            var req = {
                method: 'POST',
                url: '/api/myresource',
                headers: {
                    'Content-Type': 'application/json',
                    'Content-Encoding': 'gzip'
                },
                data: binaryString,
                transformRequest: []
            }

            $http(req).then(function (result) {
                alert('We have just successfully sent a gzip encoded payload to the server');
            }, function () {
                alert('OOPS, something went wrong, checkout the Network tab in your browser for more details');
            });
        }]);
    </script>
</body>
</html>

好的,基本上我们现在已经讨论了客户端发送部分,它使用AJAX请求并指定适当的内容-编码请求头。

现在让我们来处理服务器端部分。让我们假设您使用托管在IIS中的Web 2。

因此,基本上,在您的Startup应用程序中将有一个ASP.NET类来引导您的Web:

代码语言:javascript
复制
public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var config = GlobalConfiguration.Configuration;
        config.MapHttpAttributeRoutes();
        app.UseWebApi(config);
        config.EnsureInitialized();
    }
}

然后,显然您有一个视图模型来映射您的有效负载到:

代码语言:javascript
复制
public class MyViewModel
{
    public string My { get; set; }
    public int[] Puper { get; set; }
    public string Awesome { get; set; }
}

以及一个Web API控制器,用于处理AJAX请求的服务器端处理程序:

代码语言:javascript
复制
public class TestController : ApiController
{
    [HttpPost]
    [Route("api/myresource")]
    public HttpResponseMessage Post(MyViewModel viewModel)
    {
        // We will simply echo out the received request object to the response
        var response = Request.CreateResponse(HttpStatusCode.OK, viewModel);
        return response;
    }
}

到目前一切尚好。不幸的是,Web不支持开箱即用的gzip请求编码。但是,由于这是一个相当可扩展的框架,您所要做的就是编写一个定制的委托处理程序,它将知道如何解压缩来自客户端的请求。

让我们从编写一个自定义HttpContent开始:

代码语言:javascript
复制
public class DecompressedHttpContent: HttpContent
{
    private readonly HttpContent _content;
    public DecompressedHttpContent(HttpContent content)
    {
        _content = content;
        foreach (var header in _content.Headers)
        {
            Headers.TryAddWithoutValidation(header.Key, header.Value);
        }
    }

    protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
        using (var originalStream = await _content.ReadAsStreamAsync())
        using (var gzipStream = new GZipStream(originalStream, CompressionMode.Decompress))
        {
            await gzipStream.CopyToAsync(stream);
        }
    }

    protected override bool TryComputeLength(out long length)
    {
        length = -1;
        return false;
    }
}

然后我们的委托处理程序:

代码语言:javascript
复制
public class GzipDecompressionHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken
    )
    {
        var isCompressedPayload = request.Content.Headers.ContentEncoding.Any(x => string.Equals(x, "gzip", StringComparison.InvariantCultureIgnoreCase));
        if (!isCompressedPayload)
        {
            return await base.SendAsync(request, cancellationToken);
        }

        request.Content = new DecompressedHttpContent(request.Content);
        return await base.SendAsync(request, cancellationToken);
    }
}

现在剩下的就是在我们的Startup类中注册这个自定义处理程序:

代码语言:javascript
复制
config.MessageHandlers.Add(new GzipDecompressionHandler());

差不多就是这样了。现在,当从客户端AJAX请求调用TestController.Post操作时,输入主体将包含适当的标头,而我们的委托处理程序将负责解码它,以便在调用Post操作时,您将得到已经反序列化的预期视图模型。

现在,您应该意识到,对于像本例中所示的小请求,使用gzip可能不会带来什么好处--您甚至可能会因为会增加有效负载的神奇gzip数字而使情况变得更糟。但是对于更大的请求,这种方法肯定会提高,减少您的请求大小,我强烈鼓励您使用gzip。

这就是这一努力的结果:

票数 16
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34251856

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档