Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NotFoundPage to Router #60970

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open

Add NotFoundPage to Router #60970

wants to merge 39 commits into from

Conversation

ilonatommy
Copy link
Member

@ilonatommy ilonatommy commented Mar 17, 2025

Description

  • Adds NotFoundPage parameter that can be used to customize the view of page rendered on NotFoundEvent.
  • Fixes the configuration without global router: then no subscription to _notFound was done, so we use redirection to a hardcoded not-found page.
  • Fixes the SSR streaming configuration. In streaming scenario we typically want to set 404 after the response already started (e.g. after request to a db). Throwing an exception in this case does not look good. Instead, we ignore the 404 there and redirect to NotFound page.
  • If SSR streaming did not start or is not enabled, we can reexecute the request and set the result url to a chosen path, using app.UseStatusCodePagesWithReExecute("/reexecution-path", createScopeForErrors: true);

Fixes #58815

@ilonatommy ilonatommy added area-blazor Includes: Blazor, Razor Components feature-blazor-routing Features related to routing in Blazor applications labels Mar 17, 2025
@ilonatommy ilonatommy added this to the .NET 10 Planning milestone Mar 17, 2025
@ilonatommy ilonatommy requested a review from javiercn March 17, 2025 14:11
@ilonatommy ilonatommy self-assigned this Mar 17, 2025
@ilonatommy ilonatommy requested a review from a team as a code owner March 17, 2025 14:11
@ilonatommy ilonatommy marked this pull request as draft March 17, 2025 14:11
@ilonatommy ilonatommy marked this pull request as ready for review March 18, 2025 15:58
@ilonatommy ilonatommy enabled auto-merge (squash) March 18, 2025 16:20
@ilonatommy ilonatommy disabled auto-merge March 18, 2025 17:07
Copy link
Member

@javiercn javiercn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

I think we will also want to update the project templates to use this feature.

In addition to it, I think there's another scenario we want to enable here which is to make sure that it works with UseStatusCodeWithReexecute

  • UseStatusCodeWithReexecute allows the app to render a page after a 404 has been set somewhere. We want that to also be possible with a Component page

I did something similar for handling errors a couple releases back. This is the PR with the change for reference #50550

  • I suspect we will need to do something similar here.

/cc: @danroth27

@ilonatommy ilonatommy requested a review from javiercn April 2, 2025 14:36
{
_isHandlingErrors = isErrorHandler;
if (IsProgressivelyEnhancedNavigation(httpContext.Request))
_hasStatusCodePage = hasStatusCodePage;
bool avoidEditingHeaders = hasStatusCodePage && httpContext.Response.StatusCode == StatusCodes.Status404NotFound;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this effectively disabling streaming renders in 404 responses? Is there a reason for us to do it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we start writing the response, we are effectively blocking the middleware's re-execution, a similar case to
#60970 (comment)
but with context.Response.HasStarted.

Copy link
Member Author

@ilonatommy ilonatommy Apr 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why it's needed: without it, we will add the dictionary key twice.

Case when we enter that condition:

  • the page that sets 404 is requested with enhance nav, e.g. we used NavigationManager.NavigateTo("page-that-sets-not-found").

The re-executed page is inheriting the headers of the original page, so IsProgressivelyEnhancedNavigation will return true. If we allow entering the condition, we will get an exception because we already edited the headers in the original page.

ArgumentException: An item with the same key has already been added. Key: ssr-framing

If the key is already there, disabling streaming renders is not a threat.

@@ -39,12 +39,17 @@ private async Task RenderComponentCore(HttpContext context)
{
context.Response.ContentType = RazorComponentResultExecutor.DefaultContentType;
var isErrorHandler = context.Features.Get<IExceptionHandlerFeature>() is not null;
var hasStatusCodePage = context.Features.Get<IStatusCodePagesFeature>() is not null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the interface we are looking for is IStatusCodeReExecuteFeature not IStatusCodePagesFeature. The latter is added all the time to the middleware, while the first one is only added by the handler when it's dealing with the re-execution code path, which I think is what we want (and is the equivalent of isErrorHandler)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This complicates the logic a bit. We need a way to avoid writing the response (so return from RenderComponentCore method before flushing) if we expect re-execution. By "expect" re-executon I mean: status code page was set, we used to have a 200 but now we have 404. At the place where we would like to detect it, IStatusCodeReExecuteFeature is unset and we have no way of preventing starting the response.

We could set IStatusCodeReExecuteFeature for in the handler subscribed to OnNotFound event: in SetNotFoundResponseAsync. This forces us to add <Reference Include="Microsoft.AspNetCore.Diagnostics" /> to Microsoft.AspNetCore.Components.Endpoints.csproj. Is this acceptable?

Otherwise, we will return too early to handle re-execution, again this condition:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-blazor Includes: Blazor, Razor Components feature-blazor-routing Features related to routing in Blazor applications
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Blazor] Add a way to specify a page as the Notfound fragment
2 participants