a0umi
a0umi

Reputation: 69

ASP.NET Core 3.0 Redirect HTTP 4XX and 5XX requests to customized error pages while keeping the error code

I'm looking to redirect HTTP requests with 4XX or 5XX error code to a custom error page, while keeping the error code at the request level. I also want to redirect exceptions to a custom error page, with an error code 500.

For that I used in my Startup file "app.UseStatusCodePagesWithReExecute("/error/{0}"); app.UseExceptionHandler("/error/500");" associated with an Error controller.

The part about exceptions works well. I also manage to redirect non-existent routes to my custom page while keeping the 404 error.

However, I can't redirect the following actions to my custom error pages:

What would be the technical solution applied to accomplish this?

Here is the Configure function of my Startup file :

app.UseStatusCodePagesWithReExecute("/error/{0}");
app.UseExceptionHandler("/error/500");

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "Error-StatusCode-Route",
        pattern: "error/{statusCode}",
        defaults: new { controller = "Error", action = "InternalServerError" }
);

endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
});

Upvotes: 1

Views: 919

Answers (2)

Vadim Rybak
Vadim Rybak

Reputation: 1697

If you are using core3, then this is a known bug. This bug will be fixed in 3.1. Here is a link to the issue: https://github.com/aspnet/AspNetCore/issues/13715

For now there is a workaround. You can add this code right after you call app.UseStatusCodePagesWithReExecute("/error/{0}");

app.Use((context, next) =>
{
    context.SetEndpoint(null);
    return next();
});

This will render your custom pages when you return NotFound or BadRequest from your controller action.

Upvotes: 0

Rena
Rena

Reputation: 36655

You could custom middleware to deal with it:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{   
    app.UseStatusCodePagesWithReExecute("/error/{0}");
    app.UseExceptionHandler("/error/500");     
    app.Use(async (context, next) =>
    {
        await next();
        var code = context.Response.StatusCode; 
        var newPath = new PathString("/error/"+code);
        var originalPath = context.Request.Path;
        var originalQueryString = context.Request.QueryString;
        context.Features.Set<IStatusCodeReExecuteFeature>(new StatusCodeReExecuteFeature()
        {
            OriginalPathBase = context.Request.PathBase.Value,
            OriginalPath = originalPath.Value,
            OriginalQueryString = originalQueryString.HasValue ? originalQueryString.Value : null,
        });

        // An endpoint may have already been set. Since we're going to re-invoke the middleware pipeline we need to reset
        // the endpoint and route values to ensure things are re-calculated.
        context.SetEndpoint(endpoint: null);
        var routeValuesFeature = context.Features.Get<IRouteValuesFeature>();
        routeValuesFeature?.RouteValues?.Clear();

        context.Request.Path = newPath;
        try
        {
            await next();
        }
        finally
        {
            context.Request.QueryString = originalQueryString;
            context.Request.Path = originalPath;
            context.Features.Set<IStatusCodeReExecuteFeature>(null);
        }              
    });
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    //...
}

For your ErrorController:

public class ErrorController : Controller
{
    // GET: /<controller>/
    public IActionResult InternalServerError()
    {
        return View();
    }
    [Route("error/404")]
    public IActionResult StatusCode404()
    {
       //redirect to the StatusCode404.cshtml
        return View();
    }
    [Route("error/400")]
    public IActionResult StatusCode400()
    {
        return View();
    }
}

Upvotes: 1

Related Questions