3
votes

I have ASP .NET Core 3.1 web site that uses razor pages, but also has single controller that inherits from ASP .NET Core Controller class. The controller handles payload and then redirects to one of the razor pages when successful. I'm noticing that when there is unhandled exception in the controller, browser displays HTTP 400 - Bad Request error, but I want the controller to simply use the exception handler I already defined, which looks like this:

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

In the above, the "/Error" path is razor page. In non-development mode, controller doesn't use my exception handler and just displays HTTP 400 - Bad Request browser error.

I managed to get it to work with exception filter class where inside OnException() method, I log the error and manually redirect to "/Error" page. The exception filter looks like this:

private class CustomExceptionFilter : IExceptionFilter
{
    private readonly IWebHostEnvironment hostingEnvironment;

    private readonly ILogger<CustomExceptionFilter> logger;

    public CustomExceptionFilter(IWebHostEnvironment hostingEnvironment, ILogger<CustomExceptionFilter> logger)
    {
        this.hostingEnvironment = hostingEnvironment;
        this.logger = logger;
    }

    public void OnException(ExceptionContext context)
    {
        if (this.hostingEnvironment.IsDevelopment())
        {
            return;
        }

        this.logger.LogError(context.Exception, string.Empty);
        context.Result = new RedirectToPageResult("/Error");
    }

Then, I defined [TypeFilter(typeof(CustomExceptionFilter))] on the controller itself. Everything appears to be working, but it seems like a lot of moving parts. Is this the only way to do it or maybe I'm missing something?

Here is Startup code:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseForwardedHeaders();

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

        app.UseHsts(options => options.MaxAge(365).IncludeSubdomains());
    }

    app.UseStaticFiles();

    app.UseCookiePolicy();

    app.UseXRobotsTag(options => options.NoIndex().NoFollow());

    app.UseXfo(options => options.SameOrigin());

    app.UseXXssProtection(options => options.EnabledWithBlockMode());

    app.UseXContentTypeOptions();

    app.UseNoCacheHttpHeaders();

    app.UseRedirectValidation();

    app.UseRouting();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
        endpoints.MapRazorPages();
    });
}

Thanks in advance!

3
Is there anything in your web.config or Startup.cs (or similar) files that might be interfering with your code? - Anthony McGrath
I couldn't find anything out of the ordinary. See the startup code in my post. - Eric

3 Answers

0
votes

Try this app.UseExceptionHandler("/Home/Error"); Reason for working please check your HomeController there should be default below action method [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); }

If it is not there you can create this method in any of your controller and provide path of that controller e.g. app.UseExceptionHandler("/YourController/Error");

0
votes

If you want to go with app.UseExceptionHandler("/Error"); then your Razor Page might look something like following.

Error.cshtml

@page "/Error"
@model Pages.Error

@{
  Layout = null;
}

<h1>An unexpected error occured. Status Code: @HttpContext.Response.StatusCode</h1>

Error.cshtml.cs

public class Error : PageModel
  {
    private readonly ILogger<Error> _logger;

    public Error(ILogger<Error> logger)
    {
      _logger = logger;
    }

    public void OnGet()
    {
      var exceptionHandlerFeature = HttpContext.Features.Get<IExceptionHandlerFeature>();
      if(exceptionHandlerFeature?.Error != null)
        _logger.LogError($"This is a message from logger: {exceptionHandlerFeature.Error.Message}");
    }
  }
0
votes

I finally figured out what happened. My controller was handling POST method. When any unhandled exceptions occurred, the code would attempt to run my exception handler. Because of the POST, it would post to my exception handler page, but that would fail because of AntiForgery request validation token missing, so it issue HTTP 400 - Bad Request, which I have seen before in my pages that use jQuery with AJAX if token was missing. So basically, to solve this issue, I'm going to add [IgnoreAntiforgeryToken] attribute to my razor error page handler.