c# - 压缩从 Angular 到 Web API 的请求

标签 c# angularjs asp.net-web-api

<分区>

我试图通过压缩从我的 Angular 客户端到 AspNet Web API 的请求来优化带宽使用。有什么办法可以实现吗?

最佳答案

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

废话不多说了,让我们开始练习吧。这里最大的挑战是在 javascript 中生成有效的 gzip 请求。一种可能性是阅读此格式的规范并滚动您自己的实现或使用一些现有的库。我发现特别有趣的一个是 pako

只需发出以下命令,即可使用 bower 在您的应用程序中进行安装:

bower install pako

现在让我们看看从客户 Angular 来看示例请求的外观。假设您想将以下 JSON 发送到服务器(作为 POST 或 PUT 动词):

{ my: 'super', puper: [456, 567], awesome: 'pako' }

您可以像使用现代浏览器中可用的普通 XMLHttpRequest 对象一样简单地实现这一点(如果您对 Angular 特定解决方案感兴趣,请阅读下文):

<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>

既然你询问了 Angular 请求,让我们使用原生 $http 对象 Angularify 这个示例 AJAX 请求:

<!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 请求并指定正确的 Content-Encoding 请求 header 的客户端发送部分。

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

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

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var config = GlobalConfiguration.Configuration;
        config.MapHttpAttributeRoutes();
        app.UseWebApi(config);
        config.EnsureInitialized();
    }
}

然后显然你有一个 View 模型将你的有效负载映射到:

public class MyViewModel
{
    public string My { get; set; }
    public int[] Puper { get; set; }
    public string Awesome { get; set; }
}

和一个 Web API Controller ,它将作为您的 AJAX 请求的服务器端处理程序:

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 API 不支持开箱即用的 gzip 请求编码。但由于这是一个非常可扩展的框架,您所要做的就是编写一个自定义委托(delegate)处理程序,它将知道如何解压缩来自客户端的请求。

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

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;
    }
}

然后是我们的委托(delegate)处理程序:

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 类中注册这个自定义处理程序:

config.MessageHandlers.Add(new GzipDecompressionHandler());

差不多就这些了。现在,当从客户端 AJAX 请求调用 TestController.Post 操作时,输入主体将包含正确的 header ,我们的委托(delegate)处理程序将负责对其进行解码,以便在调用 Post 操作时,您将获得已经反序列化的预期 View 模型.

现在回顾一下,您应该意识到,对于像本例中所示的小请求,您可能不会通过使用 gzip 获益太多——您甚至可能使事情变得更糟,因为神奇的 gzip 数字将添加到有效载荷。但是对于更大的请求,这种方法肯定会减少您的请求大小,我强烈建议您使用 gzip。

这是这项努力的结果:

enter image description here

关于c# - 压缩从 Angular 到 Web API 的请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34251856/

相关文章:

javascript - 当对象键值在数组中时如何过滤数组

javascript - NodeJS 服务器在检索部分时返回 505/404 错误

c# - 使用 ASP Identity 2 POSTing to/Token 端点时总是收到 'invalid_client' 错误

c# - 为什么代码在查询数据库时会耗尽 CPU?

c# - 分页只是跳过一行

c# - Outlook 2010 加载项 : how to go through all mail messages?

javascript - 如何在 Web API 中检查文件下载是否完成

c# - Expander扩展按钮wpf的移动位置

javascript - 仅使用 AngularJs 单击元素外部时如何关闭元素?

javascript - 从 Dropzone 获取上传的文件