Umbraco-CMS: UseStatusCodePages should not be set by Umbraco

Which exact Umbraco version are you using? For example: 9.0.1 - don’t just write v9

All v9 versions

Bug summary

We are currently working on a v9 website and we are having an issue rewriting static files that are returning a 404. In v8 (and .net framework), this would be done with the following setting in web.config: image

With .Net Core, we don’t have this setting anymore, but we do have the UseStatusCodePages that can be used for this. According to https://docs.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-6.0, we can use something like app.UseStatusCodePagesWithReExecute("/{0}"); to redirect every 404 error to /404. Now when we go to something like test.jpg (which doesn’t exist), it’ll show this screen: image

However, this function doesn’t work with the current Umbraco installation. This is because Umbraco already does UseStatusCodePages themselves: https://github.com/umbraco/Umbraco-CMS/blob/5bfab13dc5a268714aad2426a2b68ab5561a6407/src/Umbraco.Web.Common/ApplicationBuilder/UmbracoApplicationBuilder.cs#L88

There are two places where we can place the UseStatusCodePagesWithReExecute(). We can place it in front app.UseUmbraco(). However, then the UseStatusCodePages will be executed before UseStatusCodePagesWithReExecute (due to how middlewares flow back) and the UseStatusCodePagesWithReExecute logic inside will not be called.

We can also place it after the app.UseUmbraco(). This way the logic of UseStatusCodePagesWithReExecute does execute, but it doesn’t work at all. This is because the function goes back into the middlewares to find the page with the given path. However, because it is placed after the app.UseUmbraco(), it will not be able to execute the Umbraco middleware to find the page.

So what is the ideal position here? We want the UseStatusCodePagesWithReExecute before the UseUmbracoCoreMiddleware, but after the AppBuilder.UseStatusCodePages(); so that isn’t really possible with the current situation.

Specifics

No response

Steps to reproduce

Using the following code snippet:

AppBuilder.UseStatusCodePages(async context =>
            {
                var newPath = new PathString(
                    string.Format(CultureInfo.InvariantCulture, "/{0}", context.HttpContext.Response.StatusCode));
                string formatedQueryString = null;
                var newQueryString = QueryString.Empty;

                var originalPath = context.HttpContext.Request.Path;
                var originalQueryString = context.HttpContext.Request.QueryString;
                // Store the original paths so the app can check it.
                context.HttpContext.Features.Set<IStatusCodeReExecuteFeature>(new StatusCodeReExecuteFeature()
                {
                    OriginalPathBase = context.HttpContext.Request.PathBase.Value,
                    OriginalPath = originalPath.Value,
                    OriginalQueryString = originalQueryString.HasValue ? originalQueryString.Value : null,
                });

                // An endpoint may have already been set. Since we're going to re-invoke the middleware pipeline we need to reset
                // the endpoint and route values to ensure things are re-calculated.
                context.HttpContext.SetEndpoint(endpoint: null);
                var routeValuesFeature = context.HttpContext.Features.Get<IRouteValuesFeature>();
                routeValuesFeature?.RouteValues?.Clear();

                context.HttpContext.Request.Path = newPath;
                context.HttpContext.Request.QueryString = newQueryString;
                try
                {
                    await context.Next(context.HttpContext);
                }
                finally
                {
                    context.HttpContext.Request.QueryString = originalQueryString;
                    context.HttpContext.Request.Path = originalPath;
                    context.HttpContext.Features.Set<IStatusCodeReExecuteFeature>(null);
                }
            });

We can go through both scenarios. You do need to have a /404 page on your website

  • Put the above snippet above app.UseUmbraco().
  • Add a breakpoint in the function. When going to “test.jpg”, you’ll see that the function isn’t called

Second scenario:

  • Put the above snippet below app.UseUmbraco().
  • Add a breakpoint in the function. When going to “test.jpg”, it’ll call the function, but it’ll not rewrite your request to /404

We also checked this with the Umbraco source code. We removed the UseStatusCodePages from the Umbraco source code and placed the snippet above UseUmbracoCoreMiddleware. This way when you go to “test.jpg”, it’ll call the function and also rewrite your request to the /404 page.

Expected result / actual result

Ideally UseStatusCodePages should not be set within Umbraco. The programmer should set this themselves in their startup. The documentation on https://docs.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-6.0 also states that this function should not be used on production websites.

This is quite a long and technical issue. We can also go through this on a Discord call if that is possible. This is something we would love to see fixed as we have to make a work around for it now.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 3
  • Comments: 17 (14 by maintainers)

Most upvoted comments

@bergmania Great news, thank you for looking into it and looking forward to its removal 😄

As there hasn’t been any activity on this in the last 3 months, I’ve gone ahead and created the PR to hopefully get some more attention to this issue. (https://github.com/umbraco/Umbraco-CMS/pull/12223)