随着移动互联网的发展,各种终端设备的流行,服务器接口要求统一,这个时候RESTful就有用武之地。RESTful一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。ASP.NET WebAPI,其核心概念就是构建REST风格的Web服务,把一起数据视为资源,无论是服务请求或者是数据操作。
HttpClient + Web Api实现Restful服务
下面实现了提交一个对象json数据到服务器上请求创建操作,对应Http的POST请求。
1) 准备需要传递给WebAPI的参数
2) 然后用System.Net.Http.StringContent把它打个包
3) 然后设置一下ContentType
4) 通过Http Post调用WebAPI得到返回结果
5) 最后将Json格式返回的结果反序列化为强类型
public ErrorCode Save() { var requestJson = JsonConvert.SerializeObject(new {name = "apple", number = 10}); HttpContent httpContent = new StringContent(requestJson); httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); HttpClient httpClient = new HttpClient(); var respJsonJson = httpClient.PostAsync("http://localhost:9000/api/order/save", httpContent).Result.Content.ReadAsStringAsync().Result; var result = JsonConvert.DeserializeObject<ErrorCode>(respJsonJson); return result; }
本文基于Http Client+ASP.NET Web Api实现Restful服务访问和提供, 主要介绍如何实现数据量大的情况下进行压缩处理。
ASP.NET Web APi 压缩
对于减少响应包的大小和提高响应速度,压缩是一种简单而有效的方式。那么如何实现对ASP.NET Web API 进行压缩呢, System.IO.Compression中提供了对应的类库——GZipStream与DeflateStream,我们只需要在HttpClient与Web API中应用它们即可。
Code View
Web Api code
这里我们使用特性来完成Web API端数据的压缩。
public class CompressAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var content = actionExecutedContext.Response.Content;
var acceptEncoding = actionExecutedContext.Request.Headers.AcceptEncoding.
Where(x => x.Value == "gzip" || x.Value == "deflate").ToList();
if (acceptEncoding != null && acceptEncoding.Count > 0 && content != null &&
actionExecutedContext.Request.Method != HttpMethod.Options)
{
var bytes = content.ReadAsByteArrayAsync().Result;
if (acceptEncoding.FirstOrDefault().Value == "gzip")
{
actionExecutedContext.Response.Content = new ByteArrayContent(CompressionHelper.GzipCompress(bytes));
actionExecutedContext.Response.Content.Headers.Add("Content-Encoding", "gzip");
}
else if (acceptEncoding.FirstOrDefault().Value == "deflate")
{
actionExecutedContext.Response.Content = new ByteArrayContent(CompressionHelper.DeflateCompress(bytes));
actionExecutedContext.Response.Content.Headers.Add("Content-encoding", "deflate");
}
}
base.OnActionExecuted(actionExecutedContext);
}
}
public static byte[] DeflateCompress(byte[] data)
{
if (data == null || data.Length < 1)
return data;
try
{
using (MemoryStream stream = new MemoryStream())
{
using (DeflateStream gZipStream = new DeflateStream(stream, CompressionMode.Compress))
{
gZipStream.Write(data, 0, data.Length);
gZipStream.Close();
}
return stream.ToArray();
}
}
catch (Exception)
{
return data;
}
}
当然如果使用GZIP压缩的话,只需要将DeflateStream改成GZipStream
HttpClient Code
对于响应内容的压缩,一般Web服务器(比如IIS)都提供了内置支持,只需在请求头中包含 Accept-Encoding: gzip, deflate ,客户端浏览器与HttpClient都提供了内置的解压支持。
HttpClient httpClient = new HttpClient(new HttpClientHandler
{
AutomaticDecompression =System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate
});
这里首先判断客户端是否启动gzip,deflate压缩,如果启用并且请求类型不是Options同时又返回数据,那么我们就压缩返回数据。
HTTP Client压缩
我们用HttpClient调用第三方Web API或者iOS App调用自己的Web API时需要提交大文本数据的时候也需要对请求的数据进行压缩。
对于请求内容的压缩,.NET中的HttpClient并没有提供内置支持,IIS也没有提供对解压的内置支持,需要自己写代码实现。同上继续使用类库——GZipStream与DeflateStream。
Code View
HttpClient code
我们需要实现一个支持压缩的HttpContent——CompressedContent
public class CompressedContent : HttpContent { private readonly HttpContent _originalContent; private readonly CompressionMethod _compressionMethod; public CompressedContent(HttpContent content, CompressionMethod compressionMethod) { if (content == null) { throw new ArgumentNullException("content"); } _originalContent = content; _compressionMethod = compressionMethod; foreach (KeyValuePair<string, IEnumerable<string>> header in _originalContent.Headers) { Headers.TryAddWithoutValidation(header.Key, header.Value); } Headers.ContentEncoding.Add(_compressionMethod.ToString().ToLowerInvariant()); } protected override bool TryComputeLength(out long length) { length = -1; return false; } protected async override Task SerializeToStreamAsync(Stream stream, TransportContext context) { if (_compressionMethod == CompressionMethod.GZip) { using (var gzipStream = new GZipStream(stream, CompressionMode.Compress, leaveOpen: true)) { await _originalContent.CopyToAsync(gzipStream); } } else if (_compressionMethod == CompressionMethod.Deflate) { using (var deflateStream = new DeflateStream(stream, CompressionMode.Compress, leaveOpen: true)) { await _originalContent.CopyToAsync(deflateStream); } } } }
主要就是重载HttpContent.SerializeToStreamAsync()方法,在其中使用相应的压缩算法进行压缩。HttpClient使用这个CompressedContent的方法如下:
var httpContent = new CompressedContent(new StringContent(requestjson, Encoding.UTF8, "application/json"), CompressionMethod.GZip); HttpResponseMessage response = httpClient.PostAsync(url, httpContent).Result;
Web Api code
这里我们需要实现一个DelegatingHandler——DecompressionHandler:
public class DecompressionHandler : DelegatingHandler { protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (request.Method == HttpMethod.Post || request.Method == HttpMethod.Put) { bool isGzip = request.Content.Headers.ContentEncoding.Contains("gzip"); bool isDeflate = !isGzip && request.Content.Headers.ContentEncoding.Contains("deflate"); if (isGzip || isDeflate) { Stream decompressedStream = new MemoryStream(); if (isGzip) { using (var gzipStream = new GZipStream(await request.Content.ReadAsStreamAsync(),CompressionMode.Decompress)) { await gzipStream.CopyToAsync(decompressedStream); } } else { using (var gzipStream = new DeflateStream(await request.Content.ReadAsStreamAsync(),CompressionMode.Decompress)) { await gzipStream.CopyToAsync(decompressedStream); } } decompressedStream.Seek(0, SeekOrigin.Begin); var originContent = request.Content; request.Content = new StreamContent(decompressedStream); foreach (var header in originContent.Headers) { request.Content.Headers.Add(header.Key, header.Value); } } } return await base.SendAsync(request, cancellationToken); } }
主要重载DelegatingHandler.SendAsync()方法,用GZipStream或DeflateStream完成解压操作。然后在WebApiConfig中应用这个DecompressionHandler即可。
小结
最后大家要注意一下数据压缩的临界区域。当被压缩的数据超过一定大小后,我们压缩才能提升性能,反之则起反作用,这个需要项目开发过程中去不断尝试来确定。同时我们可以使用抓包工具来抓取一下压缩前后的数据大小,以验证我们压缩的实际效果。