ygoe
ygoe

Reputation: 20384

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

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).

Upvotes: 5

Views: 5047

Answers (3)

Josh Noe
Josh Noe

Reputation: 2797

This is a bug in .NET, up to .NET 7.

If you use app.UseDeveloperExceptionPage(), then context.HttpContext.Features.Get<IExceptionHandlerFeature>() will return null.

This appears to be fixed in .NET 8.

Upvotes: 1

mj1313
mj1313

Reputation: 8459

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
    });

    //...
}

Upvotes: 2

Matt Ghafouri
Matt Ghafouri

Reputation: 1577

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;
   ...
}

Upvotes: 4

Related Questions