Asp .Net Core How to handle error pages in area

Viewed 1505
if (env.IsDevelopment())
{ 
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Administration/Error");
    app.UseExceptionHandler("/Production/Error");
}

How do I define 2 error handling pages for different areas on razor pages?

5 Answers

An alternative to a custom exception handler page is to provide a lambda to UseExceptionHandler. Using a lambda allows access to the path of the request that made the error before returning the response.

Here's an example of using a lambda for exception handling:

app.UseExceptionHandler(errorApp =>
{
    errorApp.Run(async context =>
    {
        var exceptionHandlerPathFeature =
            context.Features.Get<IExceptionHandlerPathFeature>();

        // Use exceptionHandlerPathFeature to process the exception (for example, 
        // logging), but do NOT expose sensitive error information directly to 
        // the client.

        if (exceptionHandlerPathFeature.Path.Contains("/Administration/"))
        {
            context.Response.Redirect("/Administration/Error");
        }

        if(exceptionHandlerPathFeature.Path.Contains("/Production/"))
        {
            context.Response.Redirect("/Production/Error");
        }
    });
});

You could refer to Handle errors in ASP.NET Core: Exception handler lambda.

Updated comment as I flubbed it the first time. Per this link, you should be able to specify an ExceptionHandler delegate as part of your call. I haven't tried it, but might solve your isssue.

 if (env.IsDevelopment())
 {          
      app.UseDeveloperExceptionPage();
 }
 else
 {
            ExceptionHandlerOptions options;
            options.ExceptionHandler = new RequestDelegate();
            app.UseExceptionHandler(options);
 }

You may also want to look at Areas as a possible solution. They allow you to define subsections of your application that implement different behaviors.

I don't know any method to achieve this via a configuration. However, you could identify original path in your error handler:

if (env.IsDevelopment())
{          
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Home/Error");
}

When an error occurs, .NET adds a IExceptionHandlerPathFeature object to the Request object. You can use it to get the path:

public class HomeController : BaseController
{
    [Route("Error")]
    public IActionResult Error()
    {
        var exceptionPathFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();

        var path = exceptionPathFeature.Path;

        if(path.Contains("/Administration/"))
            return View("AdministrationErrorPage");

        if(path.Contains("/Production/"))
            return View("ProductionErrorPage");

        return View("GenericErrorPage");
    }
}

Another way of achieving your desired behavior is to use UseWhen, something like the following:

if (env.IsDevelopment())
{ 
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseWhen(
        context => context.Request.Path.StartsWithSegments("/Administration", StringComparison.OrdinalIgnoreCase)),
        appBuilder => appBuilder.UseExceptionHandler("/Administration/Error"));

    app.UseWhen(
        context => context.Request.Path.StartsWithSegments("/Production", StringComparison.OrdinalIgnoreCase)),
        appBuilder => appBuilder.UseExceptionHandler("/Production/Error"));

    // the catch-all is a bit tricky and is needs to be updated if you add a new area.
    app.UseWhen(
        context => !context.Request.Path.StartsWithSegments("/Administration", StringComparison.OrdinalIgnoreCase)) &&
                   !context.Request.Path.StartsWithSegments("/Production", StringComparison.OrdinalIgnoreCase)),
        appBuilder => appBuilder.UseExceptionHandler("/Error"));
}

assuming you have your area mapped as a route value, add the following in Configure:

app.UseStatusCodePages(context =>
{
    var area = context.HttpContext.Request.RouteValues["area"]?.ToString();
    var statusCode = context.HttpContext.Response.StatusCode;

    var location = (area, statusCode) switch {
        (string a, 404) => $"/{a}/page-not-found",
        (string a, int s) => $"/{a}/error/{s}",
        (null, 404) => "/page-not-found",
        (null, int s) => $"/error/{s}",
    };

    context.HttpContext.Response.Redirect(location);
    return Task.CompletedTask;
});
Related