某些 404 响应缺少内容

Content is missing for some 404 responses

我正在尝试 return 来自 web.api 的自定义错误响应。让它成为格式为 json 的简单字符串 "Oops!"。所以我创建了简单的委托处理程序来替换这样的错误响应:

public class ErrorMessageHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
       HttpRequestMessage request, CancellationToken cancellationToken)
    {
       var response = await base.SendAsync(request, cancellationToken);

       if (response.IsSuccessStatusCode)
           return response;

       var formatter = new JsonMediaTypeFormatter();
       var errorResponse = request.CreateResponse(response.StatusCode, "Oops!", formatter);
       return errorResponse;
    }
}

接下来我要确保这是管道中唯一的消息处理程序:

httpConfig.MessageHandlers.Clear();
httpConfig.MessageHandlers.Add(new ErrorMessageHandler());

// Only default route
httpConfig.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );

app.UseWebApi(httpConfig); // OWIN self-hosting

控制器也是最简单的一个:

public class ValuesController : ApiController
{
   public IHttpActionResult Get(int id)
   {
      if (id == 42)
        return Ok("Answer to the Ultimate Question of Life, the Universe, and Everything");

      return NotFound();
   }
}

有趣的是:

最后一个案例是我的问题。当我在委托处理程序的最后一行设置断点时,我清楚地看到 errorResponse 包含具有正确值的 ObjectContent<string>。但是为什么这个值后来被清除了?

原因是 HttpMessageHandlerAdapter.InvokeCore 中的这段代码(所以基本上在 UseWebApi 中间件中):

response = await _messageInvoker.SendAsync(request, cancellationToken);
// ...
if (IsSoftNotFound(request, response)) {
       callNext = true;
}
else { 
    // ...
}

其中 IsSoftNotFound 是:

private static bool IsSoftNotFound(HttpRequestMessage request, HttpResponseMessage response)
{
    if (response.StatusCode == HttpStatusCode.NotFound)
    {
        bool routingFailure;
        if (request.Properties.TryGetValue<bool>(HttpPropertyKeys.NoRouteMatched, out routingFailure)
            && routingFailure)
        {
            return true;
        }
    }
    return false;
}

所以基本上在 "soft" 404 的情况下,其中 "soft" 表示没有路由匹配(并且由 属性 和 request.Properties 中的特定键表示) - 中间件将调用下一个组件。否则 - 只会发送响应。

IsSoftDelete 对于你的情况是正确的(因为确实没有匹配的路由)并且下一个组件(没有时间弄清楚那是什么)清除你的响应内容。

对于 "fix" 这个问题 - 在请求被前一个处理程序处理后从请求中删除带有该键的 属性:

public class ErrorMessageHandler : DelegatingHandler {
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken) {
        var response = await base.SendAsync(request, cancellationToken);

        if (response.IsSuccessStatusCode)
            return response;

        // here, can also check if 404
        request.Properties.Remove(HttpPropertyKeys.NoRouteMatched);
        var formatter = new JsonMediaTypeFormatter();
        var errorResponse = request.CreateResponse(response.StatusCode, "Oops!", formatter);
        return errorResponse;
    }
}