MVC 6 WebAPI returns html error page instead of json version of exception object

I call the api endpoint in MVC 6 WebAPI:

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 

In the body, I have a serialized test json object.

I have an error in the code of the entity controller, and the api returns a 500 "Server Error" response. I know that the error will be fixed, but the problem I need help with is that the API returns HTML instead of a json-serialized exception object - Json is what I expect: this is what the old webapi will return. I ported the encoding from an old test project, which, as I know, works.

So why does MVC 6 WebAPI return html and not json? Is there some kind of configuration I need to do?

EDIT: I added Accept: application / json to the headers as suggested by @danludwig, however this did not solve the problem, I still got the html error page.

I looked at my StartUp.cs and found:

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

in the ConfigureApp method. I tested the app app.UseDeveloperExceptionPage (); commented out. This prevented the html error page from returning to the api response body, however I still don't get the serialized json object.

+5
source share
2 answers

ExceptionHandlerMiddleware configured when using UseExceptionHandler("Home/Error") does not contain JSON support. It will just return the html error page. The same can be said when using UseDeveloperExceptionPage .

As far as I know, you will need to add yourself a piece of code that will handle errors and return json.

  • One option is to use an exception filter and add it either globally or selected controllers, although this approach will cover exceptions that come from controller action methods. For example, the following filter will return a json object only if the request / application was json (otherwise this would allow the exception to go through which, for example, the global error page could be processed):

     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()); }); 
  • Another option is to add your own exception handler middleware using the app.UseExceptionHandler overload, which allows you to specify the behavior of an alternative pipeline that will handle the exception. I quickly wrote a similar example using the built-in middleware that will return a json object only when the request was received by the / json application:

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

Both approaches will allow you to have other error handlers that can provide html pages for non json requests (Another idea is to either return a json or html page from your custom error handler).

PS. If you are using the second approach, most likely you will want to include this logic in your own middleware class and use a different approach to generate a json response. In this case, look what JsonResultExecutor does

+5
source

I found a cheap hack to get what I want by adding this to the Startup Configure method:

 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. } 
+1
source

Source: https://habr.com/ru/post/1242450/


All Articles