一种可能性是使用行业标准算法来压缩数据,例如gzip。它们为原始字符串提供了非常好的压缩,如果您将大型对象发送到服务器,那么您肯定可以通过减少请求的大小来获得性能。更不用说当您的应用在带宽有限的移动设备上运行时所获得的好处了。
话不多说,让我们开始练习吧。这里最大的挑战是在 javascript 中生成有效的 gzip 请求。一种可能性是阅读这种格式的规范并推出您自己的实现或使用一些现有的库。我觉得特别有趣的是pako。
只需发出以下命令,即可使用bower 在您的应用程序中进行安装:
bower install pako
现在让我们看看从客户端的角度来看示例请求的外观。假设您希望将以下 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 对象对这个示例 AJAX 请求进行 Angular 化:
<!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 请求标头的客户端发送部分。
现在让我们处理服务器端部分。假设您使用托管在 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();
}
}
然后显然您有一个视图模型可以将您的有效负载映射到:
public class MyViewModel
{
public string My { get; set; }
public int[] Puper { get; set; }
public string Awesome { get; set; }
}
还有一个 Web API 控制器,它将为您的 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 请求编码。但由于这是一个非常可扩展的框架,您所要做的就是编写一个自定义委托处理程序,该处理程序将知道如何解压缩来自客户端的请求。
让我们从编写一个自定义的 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;
}
}
然后是我们的委托处理程序:
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 操作时,输入主体将包含正确的标头,我们的委托处理程序将负责对其进行解码,以便在调用 Post 操作时,您将获得已反序列化的预期视图模型.
现在回顾一下,您应该注意,对于本例中所示的小请求,您可能不会通过使用 gzip 获得太多收益 - 您甚至可能使事情变得更糟,因为 gzip 的神奇数字会添加到有效载荷。但是对于更大的请求,这种方法肯定会减少您的请求大小,我强烈建议您使用 gzip。
这是这项努力的结果: