MVC 6 WebAPI 返回 html 错误页面而不是 json 版本的异常 object
MVC 6 WebAPI returning html error page instead of json version of exception object
我正在 MVC 6 Web 中调用 api 端点API:
POST http://localhost:57287/mytestapi/testentity/ HTTP/1.1
Accept: application/json
X-APIKey: 00000000-0000-0000-0000-000000000000
Content-Type: application/json; charset=utf-8
Host: localhost:57287
Content-Length: 1837
Expect: 100-continue
Connection: Keep-Alive
在 body 我有 json 序列化测试实体。
我的实体控制器代码中有一个错误,api 是 return 一个 500 响应 'Server Error' 我知道这个错误是什么会修复它,但是问题我需要一些帮助 API 是 returning HTML 而不是 json 序列化异常 object - Json 是我所期望的: 这就是旧网站 api 会 return 的情况。我从一个我知道有效的旧测试项目移植了代码。
那么为什么 MVC 6 WebAPI returning html 而不是 json?我需要做一些配置吗?
编辑:
我按照@danludwig 的建议将 Accept: application/json 添加到 headers,但这并没有解决问题,我仍然收到 html 错误页面。
我查看了我的 StartUp.cs 并发现:
if (env.IsDevelopment())
{
//app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
在 ConfigureApp 方法中。我测试了 app.UseDeveloperExceptionPage();注释掉了。这阻止了 api 响应 body 中 html 错误页面的 return,但是我仍然没有得到 json 序列化异常 object .
ExceptionHandlerMiddleware
configured when using UseExceptionHandler("Home/Error")
does not include any support for JSON. It will just return the error html page. The same can be said when using UseDeveloperExceptionPage
.
据我所知,您需要自己添加一些代码来处理错误和 return json.
一种选择是使用异常过滤器并将其添加到全局或选定的控制器上,尽管这种方法只能涵盖来自控制器操作方法的异常。例如,仅当请求接受为 application/json 时,以下过滤器才会 return 一个 json 对象(否则它会让异常通过,例如可以由全局错误页面处理) :
public class CustomJSONExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
if (context.HttpContext.Request.GetTypedHeaders().Accept.Any(header => header.MediaType == "application/json"))
{
var jsonResult = new JsonResult(new { error = context.Exception.Message });
jsonResult.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
context.Result = jsonResult;
}
}
}
services.AddMvc(opts =>
{
//Here it is being added globally.
//Could be used as attribute on selected controllers instead
opts.Filters.Add(new CustomJSONExceptionFilter());
});
另一种选择是使用 app.UseExceptionHandler
重载添加您自己的异常处理程序中间件,它允许您指定将处理异常的替代管道的行为。我已经使用内联中间件快速编写了一个类似的示例,只有当请求接受是 application/json 时,它才会 return 一个 json 对象:
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseExceptionHandler(appBuilder =>
{
appBuilder.Use(async (context, next) =>
{
var excHandler = context.Features.Get<IExceptionHandlerFeature>();
if (context.Request.GetTypedHeaders().Accept.Any(header => header.MediaType == "application/json"))
{
var jsonString = string.Format("{{\"error\":\"{0}\"}}", excHandler.Error.Message);
context.Response.ContentType = new MediaTypeHeaderValue("application/json").ToString();
await context.Response.WriteAsync(jsonString, Encoding.UTF8);
}
else
{
//I haven't figured out a better way of signally ExceptionHandlerMiddleware that we can't handle the exception
//But this will do the trick of letting the other error handlers to intervene
//as the ExceptionHandlerMiddleware class will swallow this exception and rethrow the original one
throw excHandler.Error;
}
});
});
这两种方法都会让您拥有其他错误处理程序,这些错误处理程序可能会为非 json 请求提供 html 页面(另一个想法是 return a json 或来自自定义错误处理程序的 html 页面。
PS。如果使用第二种方法,您很可能希望将该逻辑放入它自己的中间件 class 并使用不同的方法来生成 json 响应。在那种情况下,看看 JsonResultExecutor
做了什么
我找到了一个便宜的 hack 来获得我想要的东西,方法是将其添加到 Startup Configure 方法中:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// Simple error page to avoid a repo dependency.
app.Use(async (context, next) =>
{
try
{
await next();
}
catch (Exception ex)
{
if (context.Response.HasStarted)
{
throw;
}
context.Response.StatusCode = 500;
context.Response.ContentType = "application/json";
var json = JToken.FromObject(ex);
await context.Response.WriteAsync(json.ToString());
}
});
//Rest of configure method omitted for brevity.
}
我正在 MVC 6 Web 中调用 api 端点API:
POST http://localhost:57287/mytestapi/testentity/ HTTP/1.1
Accept: application/json
X-APIKey: 00000000-0000-0000-0000-000000000000
Content-Type: application/json; charset=utf-8
Host: localhost:57287
Content-Length: 1837
Expect: 100-continue
Connection: Keep-Alive
在 body 我有 json 序列化测试实体。
我的实体控制器代码中有一个错误,api 是 return 一个 500 响应 'Server Error' 我知道这个错误是什么会修复它,但是问题我需要一些帮助 API 是 returning HTML 而不是 json 序列化异常 object - Json 是我所期望的: 这就是旧网站 api 会 return 的情况。我从一个我知道有效的旧测试项目移植了代码。
那么为什么 MVC 6 WebAPI returning html 而不是 json?我需要做一些配置吗?
编辑: 我按照@danludwig 的建议将 Accept: application/json 添加到 headers,但这并没有解决问题,我仍然收到 html 错误页面。
我查看了我的 StartUp.cs 并发现:
if (env.IsDevelopment())
{
//app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
在 ConfigureApp 方法中。我测试了 app.UseDeveloperExceptionPage();注释掉了。这阻止了 api 响应 body 中 html 错误页面的 return,但是我仍然没有得到 json 序列化异常 object .
ExceptionHandlerMiddleware
configured when using UseExceptionHandler("Home/Error")
does not include any support for JSON. It will just return the error html page. The same can be said when using UseDeveloperExceptionPage
.
据我所知,您需要自己添加一些代码来处理错误和 return json.
一种选择是使用异常过滤器并将其添加到全局或选定的控制器上,尽管这种方法只能涵盖来自控制器操作方法的异常。例如,仅当请求接受为 application/json 时,以下过滤器才会 return 一个 json 对象(否则它会让异常通过,例如可以由全局错误页面处理) :
public class CustomJSONExceptionFilter : ExceptionFilterAttribute { public override void OnException(ExceptionContext context) { if (context.HttpContext.Request.GetTypedHeaders().Accept.Any(header => header.MediaType == "application/json")) { var jsonResult = new JsonResult(new { error = context.Exception.Message }); jsonResult.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError; context.Result = jsonResult; } } } services.AddMvc(opts => { //Here it is being added globally. //Could be used as attribute on selected controllers instead opts.Filters.Add(new CustomJSONExceptionFilter()); });
另一种选择是使用
app.UseExceptionHandler
重载添加您自己的异常处理程序中间件,它允许您指定将处理异常的替代管道的行为。我已经使用内联中间件快速编写了一个类似的示例,只有当请求接受是 application/json 时,它才会 return 一个 json 对象:if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseExceptionHandler(appBuilder => { appBuilder.Use(async (context, next) => { var excHandler = context.Features.Get<IExceptionHandlerFeature>(); if (context.Request.GetTypedHeaders().Accept.Any(header => header.MediaType == "application/json")) { var jsonString = string.Format("{{\"error\":\"{0}\"}}", excHandler.Error.Message); context.Response.ContentType = new MediaTypeHeaderValue("application/json").ToString(); await context.Response.WriteAsync(jsonString, Encoding.UTF8); } else { //I haven't figured out a better way of signally ExceptionHandlerMiddleware that we can't handle the exception //But this will do the trick of letting the other error handlers to intervene //as the ExceptionHandlerMiddleware class will swallow this exception and rethrow the original one throw excHandler.Error; } }); });
这两种方法都会让您拥有其他错误处理程序,这些错误处理程序可能会为非 json 请求提供 html 页面(另一个想法是 return a json 或来自自定义错误处理程序的 html 页面。
PS。如果使用第二种方法,您很可能希望将该逻辑放入它自己的中间件 class 并使用不同的方法来生成 json 响应。在那种情况下,看看 JsonResultExecutor
做了什么
我找到了一个便宜的 hack 来获得我想要的东西,方法是将其添加到 Startup Configure 方法中:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// Simple error page to avoid a repo dependency.
app.Use(async (context, next) =>
{
try
{
await next();
}
catch (Exception ex)
{
if (context.Response.HasStarted)
{
throw;
}
context.Response.StatusCode = 500;
context.Response.ContentType = "application/json";
var json = JToken.FromObject(ex);
await context.Response.WriteAsync(json.ToString());
}
});
//Rest of configure method omitted for brevity.
}