某些 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();
}
}
有趣的是:
/api/values/42
给我 200 个响应,值为 string
/api/values/13
使用我的自定义 "Oops!"
字符串 给我 404 响应
/api/values/42/missing
给我 empty 404 响应
最后一个案例是我的问题。当我在委托处理程序的最后一行设置断点时,我清楚地看到 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;
}
}
我正在尝试 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();
}
}
有趣的是:
/api/values/42
给我 200 个响应,值为 string/api/values/13
使用我的自定义"Oops!"
字符串 给我 404 响应
/api/values/42/missing
给我 empty 404 响应
最后一个案例是我的问题。当我在委托处理程序的最后一行设置断点时,我清楚地看到 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;
}
}