【问题标题】:Content is missing for some 404 responses某些 404 响应缺少内容
【发布时间】:2018-04-12 18:38:20
【问题描述】:

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

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();
   }
}

这里很有趣:

  • /api/values/42 给了我 200 个带有值字符串的响应
  • /api/values/13 用我的自定义 "Oops!" 字符串给我 404 响应
  • /api/values/42/missing 给我 empty 404 响应

最后一种情况是我的问题。当我在委托处理程序的最后一行设置断点时,我清楚地看到errorResponse 包含具有正确值的ObjectContent&lt;string&gt;。但是为什么这个值稍后会被清除呢?

【问题讨论】:

  • 那么您确定“/api/values/42/missing”会命中您的处理程序吗?我在我的环境中进行了测试(与您的环境不同,但仍然如此),对我来说,这个处理程序完全被绕过,响应由 IIS 提供。
  • 可能是路由问题?我必须做一些阅读,但也许 DelegatingHandler 正在执行,但下游事件由于未知路由而挤压响应?根据Asp.Net docs,HttpRoutingDispatcher 似乎在自定义处理程序之后运行。
  • @Evk 是的,完全确定。处理程序得到HttpError 的响应,状态为"No route data was found for this request."。然后这个响应被新的errorResponse 替换并返回到管道(我想它应该去HttpServer)。也许是自己的问题?
  • @claylong 我也有同样的想法,但快速检查 asp.net 资源和web.api message lifecycle 海报让我确信调度员会追踪所有处理程序

标签: c# asp.net-web-api asp.net-web-api2 owin delegatinghandler


【解决方案1】:

原因是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;
}

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

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

要“修复”此问题 - 在请求由前一个处理程序处理后,使用该键从请求中删除属性:

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;
    }
}

【讨论】:

  • 天哪!我检查了包括 HttpServer 在内的所有内容,但问题确实在 owin 方面。我将进一步调查他们为什么实施这种奇怪的“未找到软件”行为。非常感谢!
  • @SergeyBerezovskiy 如果您发现未找到的软件是关于什么的 - 请在此处评论或添加新答案,因为我也有兴趣知道。
猜你喜欢
  • 1970-01-01
  • 2013-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-07
  • 2020-10-10
  • 2017-09-06
  • 1970-01-01
相关资源
最近更新 更多