Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning exceptions as JSON messages

I am developing an API with ASP.NET Core and I am struggling with the exception handling.

When any exception occurs, or in any controller where I want to return custom errors with different status codes, I want to return JSON-formatted exception reports. I do not need HTML in the error response.

I'm not sure if I should use middleware for this, or something else. How should I return JSON exceptions in an ASP.NET Core API?

like image 619
Hinrich Avatar asked Jul 29 '16 17:07

Hinrich


People also ask

How do you handle exceptions in Webapi?

You can customize how Web API handles exceptions by writing an exception filter. An exception filter is executed when a controller method throws any unhandled exception that is not an HttpResponseException exception.

What does JSON error mean?

It means that the editor failed to get a response to the server or the response wasn't in a valid JSON format. Basically, if the editor can't communicate with the server, it will show this error message instead. To fix the problem, you essentially need to fix whatever is getting in the way of the communication.

How do you handle JSON errors?

The most common way to handle JSON parse error is using try-catch block. If the JSON string is valid, it will return a JavaScript object. If the JSON string is invalid, it will throw a SyntaxError.

What is the correct syntax for throwing a HTTP response exception?

ResponseStatusException, constructor arguments: status – an HTTP status set to the HTTP response. reason – a message explaining the exception set to the HTTP response. cause – a Throwable cause of the ResponseStatusException.


2 Answers

An exception filter (either as an attribute, or a global filter) is what you are looking for. From the docs:

Exception filters handle unhandled exceptions, including those that occur during controller creation and model binding. They are only called when an exception occurs in the pipeline. They can provide a single location to implement common error handling policies within an app.

If you want any unhandled exception to be returned as JSON, this is the simplest method:

public class JsonExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        var result = new ObjectResult(new
        {
            code = 500,
            message = "A server error occurred.",
            detailedMessage = context.Exception.Message
        });

        result.StatusCode = 500;
        context.Result = result;
    }
}

You can customize the response to add as much detail as you want. The ObjectResult will be serialized to JSON.

Add the filter as a global filter for MVC in Startup:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        options.Filters.Add(typeof(JsonExceptionFilter));
    });
}
like image 87
Nate Barbettini Avatar answered Nov 08 '22 07:11

Nate Barbettini


Ok, I got a working solution, that I am pretty happy with.

  1. Add middleware: In the Configure Method, register the middleware (comes with ASP.NET Core).

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        // logging stuff, etc.
    
        app.UseStatusCodePagesWithReExecute("/error/{0}");
        app.UseExceptionHandler("/error");
    
        app.UseMvc(); // if you are using Mvc
    
        // probably other middleware stuff 
    }
    
  2. Create a Class for Messages Write a simple class that represents instances of JSON Error Messages you want to send as a request in any error case:

    public class ExceptionMessageContent
    {
    
        public string Error { get; set; }
        public string Message { get; set; }
    
    }
    
  3. Create Error Controller add the Error Controller that handles all expected and unexpected errors. Note, that these routes correspond to the middleware configuration.

    [Route("[controller]")]
    public class ErrorController : Controller
    {
    
        [HttpGet]
        [Route("")]
        public IActionResult ServerError()
        {
    
            var feature = this.HttpContext.Features.Get<IExceptionHandlerFeature>();
            var content = new ExceptionMessageContent()
            {
                Error = "Unexpected Server Error",
                Message = feature?.Error.Message
            };
            return Content( JsonConvert.SerializeObject( content ), "application/json" );
    
        }
    
    
        [HttpGet]
        [Route("{statusCode}")]
        public IActionResult StatusCodeError(int statusCode)
        {
    
            var feature = this.HttpContext.Features.Get<IExceptionHandlerFeature>();
            var content = new ExceptionMessageContent() { Error = "Server Error", Message = $"The Server responded with status code {statusCode}" };
            return Content( JsonConvert.SerializeObject( content ), "application/json" );
    
        }
    }
    

Now, when I want to throw an error anywhere, I can just do that. The request gets redirected to the error handler and sends a 500 with a nice formatted error message. Also, 404 and other codes are handled gracefully. Any custom status codes I want to send, I can also return them with an instance of my ExceptionMessageContent, for example:

// inside controller, returning IActionResult

var content = new ExceptionMessageContent() { 
    Error = "Bad Request", 
    Message = "Details of why this request is bad." 
};

return BadRequest( content );
like image 6
Hinrich Avatar answered Nov 08 '22 07:11

Hinrich