Jakub Ostrowski
Jakub Ostrowski

Reputation: 45

Chain of responsibility in ExceptionMiddleware .net core

is it possible to implement something similar to chain of responsibility pattern in .net core middleware which catches exceptions? Because I wanted to Handle exceptions globally and take them to their handlers. Example

try
{
}
catch(CustomException1 ex)
{
}
catch(CustomException2 ex)
{
}
...

The middleware grows really fast and it will be hard to maintain later. I wanted to try{} catch(Exception e) { Handle(e); } and make Handlers for each Exception, for example handler for NullReference etc. I though about solution to take the exception by type and handle it in the handle() method in the specified handler.

Upvotes: 1

Views: 970

Answers (2)

PmanAce
PmanAce

Reputation: 4303

I'm toying around with middleware, so in startup:

app.UseMiddleware<ErrorHandlingMiddleware>();

Middleware, I have one general exception handler, you could add many here (sample code, Sentry is an error log service...sentry.io):

public class ErrorHandlingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IHub _sentry;

    public ErrorHandlingMiddleware(RequestDelegate next, IHub sentry)
    {
        _sentry = sentry;
        _next = next;
    }

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

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

        if (exception is ValueNotAcceptedException) code = HttpStatusCode.NotAcceptable;
        /*if (exception is MyNotFoundException) code = HttpStatusCode.NotFound;
        else if (exception is MyUnauthorizedException) code = HttpStatusCode.Unauthorized;
        else if (exception is MyException) code = HttpStatusCode.BadRequest;*/

        // send to Sentry.IO
        _sentry.CaptureException(exception);

        var result = JsonConvert.SerializeObject(new { error = exception.Message });

        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)code;

        return context.Response.WriteAsync(result);
    }

Note adding a dependency in the constructor will make it a singleton, last the life-cycle of the app (in my case it's fine), or else add dependency in the Invoke.

Upvotes: 2

Adem Catamak
Adem Catamak

Reputation: 2009

You can create multiple exception handler IExceptionFilter. After create filters you can inject thats Mvc filters at startup.

Note: mvc filters get hit later than custom middleware. enter image description here

Note: first added filter get hit last.

services.AddMvc(options =>
{
    options.Filters.Add<GeneralExceptionFilter>();
    options.Filters.Add<DbExceptionFilter>();
});

Note: if you decide filter should not handle exception, you should not throws exception. You should set ExceptionHandled false

public void OnException(ExceptionContext context)
{
      ...
      context.ExceptionHandled = false;
}

You can also create .net core middlewares and inject it from startup. Concepts are pretty similar to mvc filters.

Upvotes: 1

Related Questions