【问题标题】:MVC 6 WebAPI returning html error page instead of json version of exception objectMVC 6 WebAPI返回html错误页面而不是json版本的异常对象
【发布时间】:2016-02-06 20:08:50
【问题描述】:

我在 MVC 6 WebAPI 中调用 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

在正文中,我有 json 序列化的测试实体。

我的实体控制器代码中有一个错误,api 返回 500 响应“服务器错误”我知道错误是什么,将修复它,但是我需要帮助的问题是 API 返回 HTML而不是 json 序列化异常对象 - Json 是我所期望的:它是旧 webapi 将返回的内容。我已经从一个我知道有效的旧测试项目中移植了代码。

那么为什么 MVC 6 WebAPI 返回的是 html 而不是 json?我需要做一些配置吗?

编辑: 我按照@danludwig 的建议将 Accept: application/json 添加到标题中,但这并没有解决问题,我仍然得到一个 html 错误页面。

我查看了我的 StartUp.cs 并发现:

if (env.IsDevelopment())
{
    //app.UseBrowserLink();
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Home/Error");
}

在 ConfigureApp 方法中。我用 app.UseDeveloperExceptionPage(); 进行了测试注释掉了。这阻止了 api 响应正文中 html 错误页面的返回,但是我仍然没有得到 json 序列化的异常对象。

【问题讨论】:

  • Accept: application/json
  • 很遗憾没有。我已经编辑了问题以显示新的标题,不过谢谢。
  • JsonOutputFormatter 是否有可能被您的代码删除?
  • 不,我在对我的测试控制器获取端点的有效调用中返回 json。只是不用于错误消息。

标签: asp.net-web-api asp.net-core-mvc


【解决方案1】:

使用UseExceptionHandler("Home/Error") 时配置的ExceptionHandlerMiddleware 不包括对JSON 的任何支持。它只会返回错误的html页面。使用UseDeveloperExceptionPage时也可以这样说。

据我所知,您需要自己添加一些处理错误并返回 json 的代码。

  • 一种选择是使用异常过滤器并将其添加到全局或选定的控制器上,尽管这种方法仅涵盖来自控制器操作方法的异常。例如,只有当请求接受是 application/json 时,以下过滤器才会返回一个 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 时才会返回一个 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 页面(另一个想法是从您的自定义错误处理程序返回 json 或 html 页面)。

PS。如果使用第二种方法,您很可能希望将该逻辑放入其自己的中间件类中,并使用不同的方法来生成 json 响应。在这种情况下,看看JsonResultExecutor 做了什么

【讨论】:

  • 这是一个很好的答案。我已经使用了它,它适用于我在控制器级别抛出的异常。然而,在 asp 核心异常处理中似乎存在更深层次的问题。我忘记为循环引用对象配置 json 格式化程序,IActionResult 由控制器返回,但在生成响应时在 asp 核心内部,json 格式化程序抛出异常,客户端收到 502 Bad Gateway 响应!获取 json 序列化异常对象是首选。坏网关在这里没有意义。你见过这个吗?解决方法?
  • 所以你的控制器返回一个 json 结果(还没有异常),当被处理时会导致异常。我想第一种方法不会抓住这个,你检查过第二种方法是否在这种情况下执行了吗?顺便说一句,当我在内联中间件手动格式化 json 时犯了一个愚蠢的错误时,我确实看到了 502。不知道为什么框架将这些视为 502!
  • 我还没有测试场景 2,但很快就会测试。真的是502让我吓坏了。如果我们有类似的东西会很好:app.UseJsonExceptionResponses();希望我能在接下来的几天里看看情景 2。
  • 第二种方法基本上是app.UseJsonExceptionResponses(),没有创建扩展方法和中间件类:)
【解决方案2】:

我发现了一个廉价的 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.
}

【讨论】:

    猜你喜欢
    • 2018-12-07
    • 2014-04-28
    • 2012-09-24
    • 2018-08-27
    • 2021-09-01
    • 1970-01-01
    • 2018-07-27
    • 1970-01-01
    • 2021-11-02
    相关资源
    最近更新 更多