Skip to content

Commit 8f234ed

Browse files
authored
Update ref doc with Not Found improvements (#36563)
1 parent 4c3534a commit 8f234ed

2 files changed

Lines changed: 55 additions & 48 deletions

File tree

aspnetcore/blazor/fundamentals/navigation.md

Lines changed: 46 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -359,49 +359,56 @@ When a component is rendered with a global interactive render mode, calling <xre
359359

360360
Use the <xref:Microsoft.AspNetCore.Components.NavigationManager.OnNotFound%2A?displayProperty=nameWithType> event for notifications when <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> is invoked. The event is only fired when <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> is called, not for any 404 response. For example, setting `HttpContextAccessor.HttpContext.Response.StatusCode` to `404` doesn't trigger <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A>/<xref:Microsoft.AspNetCore.Components.NavigationManager.OnNotFound%2A>.
361361

362-
Apps that implement a custom router can also use <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A>. The custom router can render Not Found content from two sources, depending on the state of the response:
362+
Apps that implement a custom router can use <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A>. There are two ways to inform the renderer what page should be rendered when <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> is called.
363363

364-
* Regardless of the response state, the re-execution path to the page can used by passing it to <xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithReExecute%2A>:
364+
The recommended approach that works regardless of the response state is to call <xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithReExecute%2A>. When <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> is called, the middleware renders the path passed to the method:
365365

366-
```csharp
367-
app.UseStatusCodePagesWithReExecute(
368-
"/not-found", createScopeForStatusCodePages: true);
369-
```
366+
```csharp
367+
app.UseStatusCodePagesWithReExecute(
368+
"/not-found", createScopeForStatusCodePages: true);
369+
```
370370

371-
* When the response has started, the <xref:Microsoft.AspNetCore.Components.Routing.NotFoundEventArgs.Path%2A?displayProperty=nameWithType> can be used by subscribing to the `OnNotFoundEvent` in the router:
371+
If you don't want to use <xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithReExecute%2A>, the app can still support <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> for responses that have already started. Subscribe to `OnNotFoundEvent` in the router and assign the Not Found page path to `NotFoundEventArgs.Path` to inform the renderer what content to render when <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> is called.
372372

373-
```razor
374-
@code {
375-
[CascadingParameter]
376-
public HttpContext? HttpContext { get; set; }
377-
378-
private void OnNotFoundEvent(object sender, NotFoundEventArgs e)
379-
{
380-
// Only execute the logic if HTTP response has started,
381-
// because setting NotFoundEventArgs.Path blocks re-execution
382-
if (HttpContext?.Response.HasStarted == false)
383-
{
384-
return;
385-
}
386-
387-
var type = typeof(CustomNotFoundPage);
388-
var routeAttributes = type.GetCustomAttributes<RouteAttribute>(inherit: true);
389-
390-
if (routeAttributes.Length == 0)
391-
{
392-
throw new InvalidOperationException($"The type {type.FullName} " +
393-
$"doesn't have a {nameof(RouteAttribute)} applied.");
394-
}
395-
396-
var routeAttribute = (RouteAttribute)routeAttributes[0];
397-
398-
if (routeAttribute.Template != null)
399-
{
400-
e.Path = routeAttribute.Template;
401-
}
402-
}
403-
}
404-
```
373+
`CustomRouter.razor`:
374+
375+
```razor
376+
@using Microsoft.AspNetCore.Components
377+
@using Microsoft.AspNetCore.Components.Routing
378+
@using Microsoft.AspNetCore.Http
379+
@implements IDisposable
380+
@inject NavigationManager NavigationManager
381+
382+
@code {
383+
protected override void OnInitialized() =>
384+
NavigationManager.OnNotFound += OnNotFoundEvent;
385+
386+
[CascadingParameter]
387+
public HttpContext? HttpContext { get; set; }
388+
389+
private void OnNotFoundEvent(object sender, NotFoundEventArgs e)
390+
{
391+
// Only execute the logic if HTTP response has started
392+
// because setting NotFoundEventArgs.Path blocks re-execution
393+
if (HttpContext?.Response.HasStarted == false)
394+
{
395+
return;
396+
}
397+
398+
e.Path = GetNotFoundRoutePath();
399+
}
400+
401+
// Return the path of the Not Found page that you want to display
402+
private string GetNotFoundRoutePath()
403+
{
404+
...
405+
}
406+
407+
public void Dispose() => NavigationManager.OnNotFound -= OnNotFoundEvent;
408+
}
409+
```
410+
411+
If you use both approaches in your app, the Not Found path specified in the `OnNotFoundEvent` handler takes precedence over the path configured in the re-execution middleware.
405412

406413
In the following example for components that adopt [interactive server-side rendering (interactive SSR)](xref:blazor/fundamentals/index#client-and-server-rendering-concepts), custom content is rendered depending on where <xref:Microsoft.AspNetCore.Components.NavigationManager.OnNotFound%2A> is called. If the event is triggered by the following `Movie` component when a movie isn't found on component initialization, a custom message states that the requested movie isn't found. If the event is triggered by the `User` component in the following example, a different message states that the user isn't found.
407414

aspnetcore/release-notes/aspnetcore-10/includes/blazor.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ If the MSBuild property is used, code that relied on <xref:Microsoft.AspNetCore.
400400

401401
### Blazor router has a `NotFoundPage` parameter
402402

403-
Blazor now provides an improved way to display a "Not Found" page when navigating to a non-existent page. You can specify a page to render when `NavigationManager.NotFound` (described in the next section) is invoked by passing a page type to the `Router` component using the `NotFoundPage` parameter. The feature supports routing, works across code Status Code Pages Re-execution Middleware, and is compatible even with non-Blazor scenarios.
403+
Blazor now provides an improved way to display a "Not Found" page when navigating to a non-existent page. You can specify a page to render when <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A?displayProperty=nameWithType> (described in the next section) is invoked by passing a page type to the `Router` component using the `NotFoundPage` parameter. The feature supports routing, works across Status Code Pages Re-execution Middleware, and is compatible even with non-Blazor scenarios.
404404

405405
The [`NotFound` render fragment](xref:Microsoft.AspNetCore.Components.Routing.Router.NotFound%2A) (`<NotFound>...</NotFound>`) isn't supported in .NET 10 or later.
406406

@@ -414,23 +414,23 @@ The [`NotFound` render fragment](xref:Microsoft.AspNetCore.Components.Routing.Ro
414414
</Router>
415415
```
416416

417-
The Blazor project template now includes a `NotFound.razor` page by default. This page automatically renders whenever `NavigationManager.NotFound` is called in your app, making it easier to handle missing routes with a consistent user experience.
417+
The Blazor project template now includes a `NotFound.razor` page by default. This page automatically renders whenever <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> is called in your app, making it easier to handle missing routes with a consistent user experience.
418418

419419
For more information, see <xref:blazor/fundamentals/navigation?view=aspnetcore-10.0#not-found-responses>.
420420

421421
### Not Found responses using `NavigationManager` for static SSR and global interactive rendering
422422

423-
The <xref:Microsoft.AspNetCore.Components.NavigationManager> now includes a `NotFound` method to handle scenarios where a requested resource isn't found during static server-side rendering (static SSR) or global interactive rendering:
423+
The <xref:Microsoft.AspNetCore.Components.NavigationManager> now includes a <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> method to handle scenarios where a requested resource isn't found during static server-side rendering (static SSR) or global interactive rendering:
424424

425-
* **Static server-side rendering (static SSR)**: Calling `NotFound` sets the HTTP status code to 404.
425+
* **Static server-side rendering (static SSR)**: Calling <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> sets the HTTP status code to 404.
426426

427427
* **Interactive rendering**: Signals the Blazor router ([`Router` component](xref:blazor/fundamentals/routing?view=aspnetcore-10.0#route-templates)) to render Not Found content.
428428

429429
* **Streaming rendering**: If [enhanced navigation](xref:blazor/fundamentals/routing?view=aspnetcore-10.0#enhanced-navigation-and-form-handling) is active, [streaming rendering](xref:blazor/components/rendering#streaming-rendering) renders Not Found content without reloading the page. When enhanced navigation is blocked, the framework redirects to Not Found content with a page refresh.
430430

431431
Streaming rendering can only render components that have a route, such as a [`NotFoundPage` assignment](#blazor-router-has-a-notfoundpage-parameter) (`NotFoundPage="..."`) or a [Status Code Pages Re-execution Middleware page assignment](xref:fundamentals/error-handling#usestatuscodepageswithreexecute) (<xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithReExecute%2A>). `DefaultNotFound` 404 content ("`Not found`" plain text) doesn't have a route, so it can't be used during streaming rendering.
432432

433-
`NavigationManager.NotFound` content rendering uses the following, regardless if the response has started or not (in order):
433+
<xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> content rendering uses the following, regardless if the response has started or not (in order):
434434

435435
* If <xref:Microsoft.AspNetCore.Components.Routing.NotFoundEventArgs.Path%2A?displayProperty=nameWithType> is set, render the contents of the assigned page.
436436
* If `Router.NotFoundPage` is set, render the assigned page.
@@ -439,22 +439,22 @@ Streaming rendering can only render components that have a route, such as a [`No
439439

440440
[Status Code Pages Re-execution Middleware](xref:fundamentals/error-handling#usestatuscodepageswithreexecute) with <xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithReExecute%2A> takes precedence for browser-based address routing problems, such as an incorrect URL typed into the browser's address bar or selecting a link that has no endpoint in the app.
441441

442-
You can use the `NavigationManager.OnNotFound` event for notifications when `NotFound` is invoked.
442+
You can use the <xref:Microsoft.AspNetCore.Components.NavigationManager.OnNotFound%2A?displayProperty=nameWithType> event for notifications when <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> is invoked.
443443

444444
For more information and examples, see <xref:blazor/fundamentals/navigation?view=aspnetcore-10.0#not-found-responses>.
445445

446446
### Support for Not Found responses in apps without Blazor's router
447447

448-
Apps that implement a custom router can use `NavigationManager.NotFound`. There are two ways to inform the renderer what page should be rendered when `NavigationManager.NotFound` is called:
448+
Apps that implement a custom router can use <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A>. There are two ways to inform the renderer what page should be rendered when <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> is called.
449449

450-
The recommended approach that works regardless of the response state is to call <xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithReExecute%2A>. When `NavigationManager.NotFound` is called, the middleware renders the path passed to the method:
450+
The recommended approach that works regardless of the response state is to call <xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithReExecute%2A>. When <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> is called, the middleware renders the path passed to the method:
451451

452452
```csharp
453453
app.UseStatusCodePagesWithReExecute(
454454
"/not-found", createScopeForStatusCodePages: true);
455455
```
456456

457-
If you don't want to use <xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithReExecute%2A>, the app can still support `NavigationManager.NotFound` for responses that have already started. Subscribe to `OnNotFoundEvent` in the router and assign the Not Found page path to `NotFoundEventArgs.Path` to inform the renderer what content to render when `NavigationManager.NotFound` is called.
457+
If you don't want to use <xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithReExecute%2A>, the app can still support <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> for responses that have already started. Subscribe to `OnNotFoundEvent` in the router and assign the Not Found page path to `NotFoundEventArgs.Path` to inform the renderer what content to render when <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> is called.
458458

459459
`CustomRouter.razor`:
460460

0 commit comments

Comments
 (0)