我试图通过压缩从我的角度客户端到AspNet Web API的请求来优化带宽使用.有没有办法实现这个目标?
一种可能性是使用行业标准算法来压缩数据,例如gzip
.它们为原始字符串提供了非常好的压缩,如果要将大对象发送到服务器,那么通过减少请求的大小,您肯定可以获得性能.更不用说当您的应用在带宽有限的移动设备上运行时获得的好处.
但足够的喋喋不休,让我们来练习.这里最大的挑战是在javascript中生成有效的gzip请求.一种可能性是阅读此格式的规范并滚动您自己的实现或使用一些现有的库.我觉得特别有趣的是pako.
bower
通过简单地发出以下命令,在您的应用程序中安装是微不足道的:
bower install pako
现在让我们看一下从客户角度看样本请求的样子.假设您希望将以下JSON发送到服务器(作为POST或PUT谓词):
{ my: 'super', puper: [456, 567], awesome: 'pako' }
您可以像使用XMLHttpRequest
现代浏览器中提供的普通对象一样简单地实现(如果您对Angular特定解决方案感兴趣,请阅读下面的内容):
并且由于您询问了Angular请求,让我们使用本机$http
对象Angularify此示例AJAX请求:
好的,基本上我们现在已经覆盖了使用AJAX请求的客户端发送部分,并指定了正确的Content-Encoding请求头.
现在让我们来处理服务器端部分.假设您使用IIS中托管的Web API 2.
所以基本上你会Startup
在你的ASP.NET应用程序中有一个类来引导你的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 TaskSendAsync( 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.
这是这项努力的结果: