Mark
Mark

Reputation: 1101

UseStatusCodePagesWithReExecute executes original request twice without error response for NotFound results

I've been having problems with UseStatusCodePagesWithReExecute in a dotnet razor pages web app whilst attempting to show a custom 404 error page for page requests that return a NotFound result. Specifically, if I return a NotFound from an OnGet method I find the same request is called again and there's never a re-execution of the path supplied to the middleware.

I'm using .NET Core 3.0 so haven't tried with previous versions, or the 3.1 previews.

I've managed to replicate the problem with a simple repro. The following will allow an invalid route to redirect to the error page (e.g. https://localhost:5001/foo), however, the route https://localhost:5001/ will get called twice and not redirect to the error page.

So the question I'm asking, is this a bug or am I missing some concept here? I've tried the related UseStatusCodePagesWithRedirects method and that does what it says it should do but I'd really like to use the ReExecute if I can.

Repro

Environment:

Steps:

  1. Create a template razor project dotnet new webapp -n myapp.
  2. Edit Index.cshtml.cs OnGet method to be the following:
        public IActionResult OnGet()
        {
            return NotFound();
        }
  1. Edit Startup.cs and add app.UseStatusCodePagesWithReExecute("/Error"); just after the if/else code block in the Configure method, like so:
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            // Added this line
            app.UseStatusCodePagesWithReExecute("/Error");

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

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapRazorPages();
            });
        }
  1. Place a breakpoint (assuming VSCode or Visual Studio 2019) in the OnGet on the first line.
  2. Run debug and F5 past the first request to the index page. The redirect to /Error doesn't occur and instead the breakpoint is hit again.
  3. Hit F5 again and the browser then shows its standard 404 instead of the error page.

Thanks in advance.

Upvotes: 6

Views: 3174

Answers (2)

DevPreSupport_MSFT
DevPreSupport_MSFT

Reputation: 282

StatusCodePages middleware has 3 methods to show custom error pages, they are UseStatusCodePages, UseStatusCodePagesWithRedirects and UseStatusCodePagesWithReExecute. If you want to return a NotFound result whenever you visit https://localhost:5001/ by editing Index.cshtml.cs OnGet method, you should use UseStatusCodePages to customize your 404 error page rather than UseStatusCodePagesWithReExecute.

Nevertheless, if you persist in using UseStatusCodePagesWithReExecute, you don't need to return a real NotFound. UseStatusCodePagesWithReExecute is for non-existing resources. Once you visit https://localhost:5001/any-non-exisisting-page, it will throw 404 and then re-execute a request for your own error page. But it's not a replacement of NotFound method. At the same time, pay attention to some conditions and restriction when using UseStatusCodePagesWithReExecute, you can refer to this article: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-3.1 .

Here's a working sample.

  1. Edit Startup.cs Configure method, like so:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
        //app.UseDeveloperExceptionPage();
        }
        else
        {
       // app.UseExceptionHandler("/Error");
        // The default HSTS value is 30 days. You may want to change this for 
        production scenarios, see https://aka.ms/aspnetcore-hsts.
       // app.UseHsts();
        }
    
       //app.UseStatusCodePages();
        app.UseStatusCodePagesWithReExecute("/Error", "?code={0}");
       // app.UseHttpsRedirection();
    
        app.UseStaticFiles();
    
        //app.UseStatusCodePagesWithRedirects("/Error");
    
        app.UseRouting();
    
        app.UseAuthorization();
    
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
        });
    
    }
    
  2. Add @page "{code?}" to Error.cshtml:

@page "{code?}"
@model ErrorModel
@{
    ViewData["Title"] = "Error";
}

<h1 class="text-danger">Error.</h1>
<h1 class="text-danger">Status Code: @Model.ErrorStatusCode</h1>

<h2 class="text-danger">An error occurred while processing your request.</h2>

@if (Model.ShowRequestId)
{
    <p>
        <strong>Request ID:</strong> <code>@Model.RequestId</code>
    </p>
}

<h3>Development Mode</h3>
<p>
    Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
</p>
<p>
    <strong>The Development environment shouldn't be enabled for deployed applications.</strong>
    It can result in displaying sensitive information from exceptions to end users.
    For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
    and restarting the app.
</p>

  1. When visit https://localhost:52602/hhh which is a non-existing page in Edge, the browser shows this page:

enter image description here

Upvotes: 4

Mike
Mike

Reputation: 1673

If you use

return new NotFoundResult();

instead of

return NotFound();

it will use the desired pipeline, only downside is there is no way to pass a message, if you want to pass a message, then you need to build you own middleware to handle that situation.

Upvotes: 2

Related Questions