Search code examples
c#.netasp.net-core.net-core

ASP.NET Core Web API exception handling with Request/Response Pattern


We want to apply global error handling with a Request/Response pattern. Current code looks as below. Goal is to bring a custom Response object into the global error handling call. Is this possible? See custom response object below, and example of global error handling.

Current Code:

public async Task<ActionResult<GetAllProductResponse>> Get(ProductRequest productRequest)
{
    try
    {
        var products = await ProductAppService.GetAllProducts();
        var response = new GetAllProductResponse { Body = products };
        return Ok(response);
    }
    catch (Exception ex)
    {
        logger.LogError(ex, ex.Message);
        var response = new GetAllProductResponse { HasError = true, Error = ex.Message };
        return StatusCode(StatusCodes.Status500InternalServerError, response);
    }
}

public class GetAllDepartmentResponse : BaseRequestResponse<IEnumerable<ProductDto>>
{
}

public class BaseRequestResponse<T>
{
    [Required, ValidateObject]
    public T Body { get; set; }
    public bool HasError { get; set; }
    public string Error { get; set; }
}
}

Goal code: How would I merge a Custom Response Object above with error Handling here? Is it possible? Want to pass as parameter object into global error handling, it can be Product, or Customer, Location, etc.

ASP.NET Core Web API exception handling

public class ErrorHandlingMiddleware
{
    private readonly RequestDelegate next;
    public ErrorHandlingMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext context /* other dependencies */)
    {
        try
        {
            await next(context);
        }
        catch (Exception ex)
        {
            await HandleExceptionAsync(context, ex);
        }
    }

    private static Task HandleExceptionAsync(HttpContext context, Exception ex)
    {
        var code = HttpStatusCode.InternalServerError; // 500 if unexpected

        if      (ex is MyNotFoundException)     code = HttpStatusCode.NotFound;
        else if (ex is MyUnauthorizedException) code = HttpStatusCode.Unauthorized;
        else if (ex is MyException)             code = HttpStatusCode.BadRequest;

        var result = JsonConvert.SerializeObject(new { error = ex.Message });
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)code;
        return context.Response.WriteAsync(result);
    }
}

Another resource: https://code-maze.com/global-error-handling-aspnetcore/


Solution

  • You can't. Well you can but its messy.

    You have two types of custom error and the client will want to be able to parse both, plus the non errored response.

    Obviously you could simply fall through when you deserialise on the client side.

    Normally you check the httpcode and if its not 200 you know to deserialse an error object. You can try the first style and then the second if that throws. But its not a great approach.

    Better to get rid of one of your custom error formats.

    Your controller error handling is the worst of the two. It wont pass through error codes such as 401 if you have a dependency which uses the same auth.

    But more seriously the wrapper simply confuses what the client has to check to see if the response is good or not. have the client throw an exception when the server returns an exception. Dont wrap it all up in a custom object and have the calling code have if(response.IsError) { throw..} littered everywhere