<分区>
我试图通过压缩从我的 Angular 客户端到 AspNet 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。
这是这项努力的结果:
关于c# - 压缩从 Angular 到 Web API 的请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34251856/