Search code examples
c#.net.net-7.0

Registering app.UseExceptionHandler results in middleware not receiving exception c# net7.0


I have a simple project that I'm trying to configure to report exceptions to an external error reporting service.

On an exception being thrown I expect the HoneybadgerMiddleware to call await client.NotifyAsync(exception). This works until I register app.UseExceptionHandler("/error").

I can see the call reaching the middlewares Invoke method, however when the expecption is thrown in the awaited context it's not returned to the Invoke method.

Please could someone explain why this is the case and what I can do to ensure the HoneybadgerMiddleware is called first on expcetion, reports on the expecption and then rethrows the exception so it's picked up by the UseExceptionHandler controller.

public class Startup
{

    public Startup(){}

    public void ConfigureServices(IServiceCollection services)
    {
        // add honeybadger startup filter and client
        services
            .AddSingleton<IStartupFilter, HoneybadgerStartupFilter>()
            .AddScoped<IHoneybadgerClient, HoneybadgerClient>(context =>
                (HoneybadgerClient)HoneybadgerSdk.Init(
                    new HoneybadgerOptions(_config.GetValue<string>("Honeybadger:ApiKey") ?? "")));

        services.AddUmbraco(_env, _config)
            .AddBackOffice()
            .AddWebsite()
            .AddComposers()
            .Build();
    }


    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {       
        app.UseExceptionHandler("/error");
    
        app.UseUmbraco()
            .WithMiddleware(u =>
            {
                u.UseBackOffice();
                u.UseWebsite();
            })
            .WithEndpoints(u =>
            {
                u.UseInstallerEndpoints();
                u.UseBackOfficeEndpoints();
                u.UseWebsiteEndpoints();
            });
    }
}


public class HoneybadgerStartupFilter : IStartupFilter
{
    public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
    {
        return builder =>
        {
            // builder.ApplicationServices.GetService<DiagnosticListener>()?.SubscribeWithAdapter(new DiagnosticSubscriber());
            builder.UseMiddleware<HoneybadgerMiddleware>();
            next(builder);
        };
    }



public class HoneybadgerMiddleware
{
    private readonly RequestDelegate _next;

    public HoneybadgerMiddleware(RequestDelegate requestDelegate)
    {
        _next = requestDelegate;
    }

    public async Task Invoke(HttpContext context, IHoneybadgerClient client)
    {
        try
        {
            await _next(context);
        }
        catch (Exception exception)
        {
            await client.NotifyAsync(exception);
            // re-throw the original exception
            throw;
        }
    }
}

Solution

  • Your IStartupFilter will be registered before the UseExceptionHandler so by the moment it will be invoked exception will already be handled. You can try registering middleware "manually" after the exception filter:

    app.UseExceptionHandler("/error");
    app.UseMiddleware<HoneybadgerMiddleware>();