Terry
Terry

Reputation: 1992

app.UseStatusCodePagesWithReExecute Does Not Seem to Execute Pipeline Again

In my .NET 7 web site, I've set up the processing pipeline to use UseStatusCodePagesWithReExecute but with a registered middleware in the pipeline returns 404, it correctly returns the 404.cshtml page, but it doesn't seem to reprocess the entire pipeline.

For example, I have a need to render a 404.cshtml page, but I have to change the response.StatusCode = 400 instead of 404.

During the pipeline construction, I do this:

app
    .Use( async ( context, next ) => {
        if ( new[] { 500, 401, 403, 404 }.Contains( context.Response.StatusCode ) )
        {
            Console.WriteLine( $"app.Use (before) StatusCode: {context.Response.StatusCode}, {context.Request.Path}" );
        }
        await next();
     } )
    .UseExceptionHandler( "/errors/handler" )
    .UseStatusCodePagesWithReExecute( "/errors/{0}" );

// register more middlewares, one which could return 404, psuedo is below...

app.Use( async ( context, next ) =>
{
    if ( context.Request.Path.StartsWithSegments( "/app/channel-homex" ) )
    {
        context.Response.StatusCode = 404;
        return;
    }
    await next();
} ).Use( async ( context, next ) =>
{
    if ( new[] { 500, 401, 403, 404 }.Contains( context.Response.StatusCode ) )
    {
        Console.WriteLine( $"app.Use (after) StatusCode: {context.Response.StatusCode}, {context.Request.Path}" );
    }
    await next();
} );

As I understand it, when I do context.Response.StatusCode = 404; return; it is supposed to start the pipeline over, however, I only get the 'after' output in the console instead of both the 'before' and the 'after'.

Am I misunderstanding pipelines or how to process return vs await next() in my middleware implementations?

UPDATE

I've made a copy an paste .NET7 Program.cs for debugging based on answer below, however it seems there are two important points I was missing.

  1. It seems that wherever you register UseStatusCodePagesWithReExecute, that is the 'restart' point of the pipeline.
  2. If I don't 'return' after resetting the StatusCode = 400 the browser still reports a return status of 404 (even though the console reports 400).

I think my only remaining question is the mystery about return vs await next() after setting status to 400. What is proper code to use?

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

app
    .UseHttpsRedirection()
    // Whereever this is registered is the 'restart' point for the pipeline
    .UseStatusCodePagesWithReExecute( "/error/{0}" )
    .Use( async ( context, next ) =>
    {
        Console.WriteLine( $"app.Use (before) StatusCode: {context.Response.StatusCode}, {context.Request.Path}" );
        await next();
    } )
    // If registered here, this is where pipeline 'restarts' so I don't get 'before' log
    // .UseStatusCodePagesWithReExecute( "/error/{0}" )
    .Use( async ( context, next ) =>
    {
        if ( new[] { 500, 401, 403, 404 }.Contains( context.Response.StatusCode ) )
        {
            Console.WriteLine( $"app.Use change StatusCode=400: Current={context.Response.StatusCode}, {context.Request.Path}" );
            context.Response.StatusCode = 400;
            // If I don't return here, browser status is reported as 404 still
            // return;
        }
        await next();
    } )
    .Use( async ( context, next ) =>
    {
        if ( context.Request.Path.StartsWithSegments( "/throw" ) )
        {
            Console.WriteLine( $"app.Use change StatusCode=404: Current={context.Response.StatusCode}, {context.Request.Path}" );
            context.Response.StatusCode = 404;
            return;
        }
        await next();
    } )
    .Use( async ( context, next ) =>
    {
        Console.WriteLine( $"app.Use (after) StatusCode: {context.Response.StatusCode}, {context.Request.Path}" );
        await next();
    } );

app.Run();

Upvotes: 0

Views: 57

Answers (1)

Brando Zhang
Brando Zhang

Reputation: 28267

The reason why you just see the after is you use the UseStatusCodePagesWithReExecute this method re-execute the request with the new path instead

I suggest you modify your middleware as below and test again:

app.UseStatusCodePagesWithReExecute("/errors/{0}");

app.Use(async (context, next) =>
{
    await next();

    if (new[] { 500, 401, 403, 404 }.Contains(context.Response.StatusCode))
    {
        Console.WriteLine($"app.Use (before) StatusCode: {context.Response.StatusCode}, {context.Request.Path}");
    }
});

app.Use(async (context, next) =>
{
    if (context.Request.Path.StartsWithSegments("/app/channel-homex"))
    {
        context.Response.StatusCode = 404;
        await next();   
    }
    else
    {
        await next();
    }
});

app.Use(async (context, next) =>
{
    await next();

    if (new[] { 500, 401, 403, 404 }.Contains(context.Response.StatusCode))
    {
        Console.WriteLine($"app.Use (after) StatusCode: {context.Response.StatusCode}, {context.Request.Path}");
    }
});

app.UseExceptionHandler("/errors/handler");

Result:

enter image description here

Upvotes: 0

Related Questions