dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.23k stars 9.95k forks source link

IExceptionHandler not being called when using UseDeveloperExceptionPage() #52622

Open jamezamm opened 9 months ago

jamezamm commented 9 months ago

Is there an existing issue for this?

Describe the bug

Program.cs:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddExceptionHandler<GlobalExceptionHandler>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}
else
{
    app.UseDeveloperExceptionPage();
}

...

app.Run();

GlobalExceptionHandler.cs:

public class GlobalExceptionHandler : IExceptionHandler
{
    public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken)
    {
        ...

        return false;
    }
}

As stated within the documentation , registered exception handlers are triggered when using either UseExceptionHandler() or UseDeveloperExceptionPage().

When in production (UseExceptionHander()) the GlobalExceptionHandler is triggered successfully, while in development (UseDeveloperExceptionPage()) the GlobalExceptionHandler is NOT triggered.

.NET Version

8

yugabe commented 9 months ago

In the default templates, the exception handler middleware is only added to the pipeline in non-development scenarios, for example:

    if (app.Environment.IsDevelopment())
    {
        app.UseWebAssemblyDebugging();
    }
    else
    {
        app.UseExceptionHandler("/error", createScopeForErrors: true);
        app.UseHsts();
    }

Additionally, in the Development environment, the DeveloperExceptionPage middleware is ambiently added to the pipeline. It will only handle unhandled Exceptions. So if you want to handle the exception yourself, you have to add app.UseExceptionHandler to the pipeline.

If you want to register it in Development environment as well, you can do so. Or you can use any of the alternative ways, like a custom (non-IExceptionHandler) middleware with try-catch.

Kahbazi commented 9 months ago

@jamezamm This is by design currently the 'IExceptionHandler' only works for the UseExceptionHandler() middleware and not the UseDeveloperExceptionPage().

Perhaps this would be a good feature to add.

jamezamm commented 9 months ago

@Kahbazi In my opinion this feature is a MUST as I cannot use Developer exception pages together with IExceptionHandler. Therefore my only option is not to use the developer exception pages middleware.

Additionally, this feature is heavily implied within the documentation. Specifically:

When the preceding code runs in the Development environment:

  • The CustomExceptionHandler is called first to handle an exception.
  • After logging the exception, the TryHandleException method returns false, so the developer exception page is shown.
Kahbazi commented 9 months ago

Additionally, this feature is heavily implied within the documentation.

This seems like a problem with docs.

Also you can use IDeveloperPageExceptionFilter for DeveloperPageException.

KennethHoff commented 9 months ago

This confused me a lot as well, and I resorted to simply remove the call to UseDeveloperExceptionPage.

Basically what I want is for the developer exception page to effectively be the last in the pipeline of exception handlers.

// Stuff before...
builder.Services.AddExceptionHandler<FirstCustomExceptionHandler>() // Handles some specific case
builder.Services.AddExceptionHandler<SecondCustomExceptionHandler>() // Handles another specific case

if (!builder.Environment.IsDevelopment())
{
    builder.Services.AddExceptionHandler<FallbackExceptionHandler>(); // Fallback handler for Prod
}

// Stuff in-between...

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler();
}
else
{
    app.UseDeveloperExceptionPage(); // Fallback handler for development
}

// Stuff after..

Maybe it could even be refactored into an exception handler á la this:

// Stuff before...

builder.Services.AddExceptionHandler<FirstCustomExceptionHandler>();
builder.Services.AddExceptionHandler<SecondCustomExceptionHandler>();

if (!builder.Environment.IsDevelopment())
{
    builder.Services.AddExceptionHandler<FallbackExceptionHandler>();
}
else
{
    builder.Services.AddExceptionHandler<DeveloperExceptionPageExceptionHandler>() // This is the built-in one.
}

// Stuff in-between...

app.UseExceptionHandler();

// Stuff after..
emetrix commented 5 months ago

I'm facing the same issue. In my opinion and refering to the docs a custom exception handing middleware should ALWAYS be called, whether or not the UseDeveloperExceptionPage() is used ?

voroninp commented 5 months ago

Why?

Why is it unhandled, if IExceptionHandler can handle it?

I see the same exception twice. The first one is from the middleware, and then it is logged by IExceptionHanlder.