ASP.NET Core UseExceptionHandler: Where's the exception?

Viewed 2123

In my ASP.NET Core 3.1 application, there's code like this in the Startup class:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseStatusCodePagesWithReExecute("/error/{0}");
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/error/500");
        app.UseHsts();
    }

    // ...
}

The ErrorController has an action like this:

public IActionResult Index(int id)
{
    var context = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
    var exception = context?.Error;
    if (exception != null)
    {
        logger.LogCritical(exception, $"Unhandled exception in request {HttpContext.TraceIdentifier}: {exception.GetRecursiveMessage()}");
    }
    else
    {
        logger.LogCritical(exception, $"Unhandled exception in request {HttpContext.TraceIdentifier}, no exception available");
    }

    // ...
}

From what I've read, this should give me the unhandled exception that occurred in another action. But it doesn't. The exception object is null. I also tried using the other feature of type IExceptionHandlerFeature but it's just the same. No exception anywhere.

How can I get the exception in the error handler?

Please note that I'm using the re-execute feature because I hate forwarding the user to a different URL in case of an error. That only makes things worse because reloading the page to "try again" will certainly never resolve any problem (it's the error page, after all, it can only ever show errors).

2 Answers

To get the exception object you could use IExceptionHandlerFeature

public IActionResult Error()
{
   // Retrieve error information in case of internal errors
   var error = HttpContext
          .Features
          .Get<IExceptionHandlerFeature>();

   if (error == null)
     ...

  // Use the information about the exception 
  var exception = error.Error;
   ...
}

The StatusCodePages middleware runs only if the response generated so far has no body.

If you want to catch exception in action, you could use a custom ExceptionHandler.

public class CustomExceptionHandler
{
    private readonly IWebHostEnvironment _environment;

    public CustomExceptionHandler(IWebHostEnvironment environment)
    {
        _environment = environment;
    }

    public async Task Invoke(HttpContext httpContext)
    {
        var feature = httpContext.Features.Get<IExceptionHandlerFeature>();
        var error = feature?.Error;

        if (error != null)
        {
            //LogError
        }

        await Task.CompletedTask;
    }
}

startup.cs:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseExceptionHandler(new ExceptionHandlerOptions
    {
        ExceptionHandler = new CustomExceptionHandler(env).Invoke
    });

    //...
}
Related