Skip to content

UseStatusCodePagesWithReExecute should respect [SkipStatusCodePages] #39472

@halter73

Description

@halter73

Describe the bug

app.UseStatusCodePagesWithReExecute(string pathFormat) must come before any explicit call to app.UseRouting() in order to redo route matching with pathFormat. Reversing this order causes requests that should result in a status code page to result in a 404 instead because the route matching is never rerun.

The problem is that correctly putting UseStatusCodePagesWithReExecute before UseRouting causes [SkipStatusCodePages] to be ignored. StatusCodePagesMiddlewarechecks for ISkipStatusCodePagesMetadata before UseRouting sets the metadata. (One possible fix might be to check for the metadata again after calling _next if GetEndpoint() returned null before calling _next.)

public void Configure(IApplicationBuilder app)
{
    app.UseStatusCodePagesWithReExecute("/status");

    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGet("/", () =>
        {
            return Results.BadRequest(); // "Status: 400" correctly written to body
        });

        endpoints.MapGet("/skip", [SkipStatusCodePages] () =>
        {
            return Results.BadRequest(); // "Status: 400" *incorrectly* written to body
        });

        endpoints.MapGet("/status", (HttpResponse response) =>
        {
            return $"Status: {response.StatusCode}"; // "Status: 200" correctly written to body when hit directly
        });
    });
}

Reversing the order leads to 404s. This is unfortunately by design, but it would be great if we could make this work similar to the way we do for the implicit UseRouting in WebApplication.

public void Configure(IApplicationBuilder app)
{
    app.UseRouting();

    app.UseStatusCodePagesWithReExecute("/status");

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGet("/", () =>
        {
            return Results.BadRequest(); // Empty 404 response
        });

        endpoints.MapGet("/skip", [SkipStatusCodePages] () =>
        {
            return Results.BadRequest(); // Empty 404 response
        });

        endpoints.MapGet("/status", (HttpResponse response) =>
        {
            return $"Status: {response.StatusCode}"; // "Status: 200" correctly written to body when hit directly
        });
    });
}

If WebApplication is used instead with its implicit call to UseRouting, everything works correctly. Rerouting does not 404 and [SkipStatusCodePages] is respected. This is thanks to @BrennanConroy's work in #35426. Maybe we could do something similar for non-WebApplication scenarios.

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseStatusCodePagesWithReExecute("/status");

// Adding app.UseRouting() here would reintroduce the broken behavior of ignoring [SkipStatusCodePages]

app.MapGet("/", () =>
{
    return Results.BadRequest(); // "Status: 400" correctly written to body
});

app.MapGet("/skip", [SkipStatusCodePages] () =>
{
    return Results.BadRequest(); // Correct empty 400 response because /status is skipped
});

app.MapGet("/status", (HttpResponse response) =>
{
    return $"Status: {response.StatusCode}"; // "Status: 200" correctly written to body when hit directly
});

app.Run();

More context from previous work in this area: #38509 (comment)

Expected Behavior

[SkipStatusCodePages] should be respected and rerouting should not 404.

public void Configure(IApplicationBuilder app)
{
    app.UseStatusCodePagesWithReExecute("/status");

    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGet("/", () =>
        {
            return Results.BadRequest(); // "Status: 400" correctly written to body
        });

        endpoints.MapGet("/skip", [SkipStatusCodePages] () =>
        {
            return Results.BadRequest(); // Correct empty 400 response because /status is skipped
        });

        endpoints.MapGet("/status", (HttpResponse response) =>
        {
            return $"Status: {response.StatusCode}"; // "Status: 200" correctly written to body when hit directly
        });
    });
}

.NET Version

7.0.100-alpha.1.22060.1

Metadata

Metadata

Assignees

Labels

area-networkingIncludes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractionsbugThis issue describes a behavior which is not expected - a bug.help wantedUp for grabs. We would accept a PR to help resolve this issue

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions