我有一个自托管的WebApi。我的客户端正在发送一个GZip压缩流,我有一个属性,它在ActionFilterAttribute的OnActionExecuting覆盖中解压缩流。
但是,控制器中的Details对象始终为null。
public class DatabaseAPIController : ApiController
{
[Decompression]
public bool PostDetails([FromBody] IEnumerable<Detail> Details)
{
// The Details object is null.
// This writes out the correct JSON which has been decompressed which proves my decompression method works.
Console.WriteLine(Request.Content.ReadAsStringAsync().Result);
// Do other work.
return true;
}
}
这是解压缩属性。
public class DecompressionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
var content = actionContext.Request.Content;
var bytes = content == null ? null : content.ReadAsByteArrayAsync().Result;
var decompressedContent = bytes == null ? new byte[0] : CompressionHelper.Decompress(bytes);
actionContext.Request.Content.Headers.Clear();
actionContext.Request.Content = new StringContent(Encoding.UTF8.GetString(decompressedContent), Encoding.UTF8, "application/json");
base.OnActionExecuting(actionContext);
}
}
如果我从客户端删除压缩,从服务器删除解压缩属性,那么一切都很好。
我不明白为什么解压缩后的JSON没有填充PostDetails方法中的Details对象。
完整答案
public class DecompressionHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
var content = request.Content;
var bytes = content == null ? null : content.ReadAsByteArrayAsync().Result;
var decompressedContent = bytes == null ? new byte[0] : CompressionHelper.Decompress(bytes);
request.Content.Headers.Clear();
request.Content = new StringContent(Encoding.UTF8.GetString(decompressedContent), Encoding.UTF8, "application/json");
request.Content.Headers.Add("Content-Length", decompressedContent.Length.ToString());
var response = await base.SendAsync(request, cancellationToken);
return response;
}
}
我在我的自托管设置的构造函数中注册了我的处理程序。
public partial class MainWebAPI : ServiceBase
{
private HttpSelfHostServer _server;
private readonly HttpSelfHostConfiguration _config;
public const string ServiceAddress = "http://localhost:2345";
public MainWebAPI()
{
InitializeComponent();
_config = new HttpSelfHostConfiguration(ServiceAddress);
_config.MessageHandlers.Add(new DecompressionHandler());
_config.Routes.MapHttpRoute("DefaultApi",
"api/{controller}/{id}",
new { id = RouteParameter.Optional });
}
我想参数绑定可能已经在OnActionExecuting期间发生了,所以无论你在那里做什么,模型绑定器都不会看到。
我认为您应该尝试使用MessageHandler:
public class CompressedMessageHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// Check for compressed message
// Change the request
// Call the inner handler.
var response = await base.SendAsync(request, cancellationToken);
return response;
}
}
消息处理程序适用于在HTTP消息级别(而不是控制器操作)操作的交叉关注点。例如,消息处理程序可能读取或修改请求标头。
这或多或少就是你在这里想要做的(内容)。
您将不再需要[Decompression]
属性(这很好);您需要使用来自客户端的自定义http头来向处理程序指示您正在接收的内容已被压缩;与发送压缩响应时发生的情况大致相同(由系统管理,而不是手动管理)。