Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 60 additions & 6 deletions aspnetcore/blazor/fundamentals/navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ author: guardrex
description: Learn about navigation in Blazor, including how to use the Navigation Manager and NavLink component for navigation.
monikerRange: '>= aspnetcore-3.1'
ms.author: wpickett
ms.date: 06/24/2026
ms.date: 09/15/2026
uid: blazor/fundamentals/navigation
---
# ASP.NET Core Blazor navigation
Expand Down Expand Up @@ -289,11 +289,13 @@ You can use the `<BlazorDisableThrowNavigationException>` MSBuild property set t

## Not Found responses

<xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A?displayProperty=nameWithType> handles scenarios where a requested resource isn't found during static server-side rendering (static SSR) or global interactive rendering:
<xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A?displayProperty=nameWithType> handles scenarios where a requested resource isn't found:

* **Static SSR**: Calling <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> sets the HTTP status code to 404.
* **Static server-side rendering (static SSR)**: Calling <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> sets the HTTP status code to 404. If Not Found content is configured through `Router.NotFoundPage` or `NotFoundEventArgs.Path`, the router renders Not Found content.

* **Interactive rendering**: Signals the Blazor router ([`Router` component](xref:blazor/fundamentals/routing#route-templates)) to render Not Found content.
* **Global interactive rendering**: Signals the Blazor router ([`Router` component](xref:blazor/fundamentals/routing#route-templates)) to render Not Found content.

* **Per-page/component interactive rendering**: Behaves the same as static server-side rendering (static SSR) during prerendering on the server, while interactive Not Found behavior no-ops.

* **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.

Expand Down Expand Up @@ -328,10 +330,10 @@ When a component is rendered statically (static SSR) and <xref:Microsoft.AspNetC
}
```

To provide Not Found content for global interactive rendering, use a Not Found page (Razor component).
To provide Not Found content for global interactive rendering or for per-page/component interactive rendering *during prerendering*, use a Not Found page (Razor component).

> [!NOTE]
> The Blazor project template includes a `NotFound.razor` page. This page automatically renders whenever <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> is called, making it possible to handle missing routes with a consistent user experience.
> The Blazor project template includes a `NotFound.razor` page and configures it as the router's `NotFoundPage`. This page renders when <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> is called in a supported context, making it possible to handle missing routes with a consistent user experience.

`Pages/NotFound.razor`:

Expand Down Expand Up @@ -375,6 +377,58 @@ When a component is rendered with a global interactive render mode, calling <xre
}
```

When a component is rendered with a per-page/component interactive render mode, calling <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> signals the Blazor router to render the `NotFound` component ***only during prerendering*** (<xref:Microsoft.AspNetCore.Components.RendererInfo.IsInteractive?displayProperty=nameWithType>):

```csharp
protected override void OnInitialized()
{
// Where your initialization code must lead to a Not Found response:
if (!RendererInfo.IsInteractive)
{
Navigation.NotFound();
}
}
Comment thread
guardrex marked this conversation as resolved.
```

The preceding example would assign a per-page/component render mode at the top of the Razor component file (for example, `@rendermode InteractiveServer`).

The Not Found response and 404 status code aren't applied outside of prerendering, so if prerendering is disabled or the <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> call is made after prerendering, the component should indicate to the user in the component's UI that a resource isn't found.

<xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> doesn't terminate the method. If <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> should end the control flow, add a `return` statement after calling <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A>. In the following movie database update method, a concurrency exception results in checking for the presence of a movie in the database to determine if a Not Found response is appropriate. The `return` statement after calling <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> ensures that the handler selects only the Not Found outcome, independently of a given database provider synchronously or asynchronously executing <xref:Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync%2A>:

```csharp
private async Task UpdateMovie()
{
using var context = DbFactory.CreateDbContext();
context.Attach(Movie!).State = EntityState.Modified;

try
{
await context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie!.Id))
{
Navigation.NotFound();

return;
}
else
{
throw;
}
}

Navigation.NavigateTo("/movies");
}
```

Services not shown in the preceding example:

* `NavigationManager` is an injected <xref:Microsoft.AspNetCore.Components.NavigationManager> (`@inject NavigationManager Navigation`).
* `DbFactory` is an injected <xref:Microsoft.EntityFrameworkCore.IDbContextFactory%601> (`@inject IDbContextFactory<{CONTEXT}> DbFactory`, where the `{CONTEXT}` placeholder is a <xref:Microsoft.EntityFrameworkCore.DbContext>).

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>.

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.
Expand Down
11 changes: 10 additions & 1 deletion aspnetcore/blazor/tutorials/movie-database-app/part-3.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
---
title: Build a Blazor movie database app (Part 3 - Learn about Razor components)
ai-usage: ai-assisted
author: guardrex
description: This part of the Blazor movie database app tutorial explains the Razor components in the project that were scaffolded into the app. Improvements are made to the display of movie data.
monikerRange: '>= aspnetcore-8.0'
ms.author: wpickett
ms.date: 11/11/2025
ms.date: 09/15/2026
uid: blazor/tutorials/movie-database-app/part-3
zone_pivot_groups: tooling
---
Expand Down Expand Up @@ -843,6 +844,14 @@ When a movie isn't found, calling <xref:Microsoft.AspNetCore.Components.Navigati

If there's a concurrency exception and the movie entity no longer exists at the time that changes are saved, the component redirects to the Not Found page, which results in returning a 404 (Not Found) status code. If the movie exists and a concurrency exception is thrown, for example when another user has already modified the entity, the exception is rethrown by the component with the [`throw` statement (C# Language Reference)](/dotnet/csharp/language-reference/statements/exception-handling-statements#the-throw-statement). Additional guidance on handling concurrency with EF Core in Blazor apps is provided by the Blazor documentation.

<!-- UPDATE 11.0 - Remove the following NOTE after the scaffolder
updates go public on
https://github.com/dotnet/Scaffolding/issues/3828.
-->

> [!NOTE]
> Later, you're instructed to make a minor modification to the preceding code in the `Edit` component's `UpdateMovie` method, where a `return` statement is added after the call to <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A>. Guidance on this change and information on why the change is required is provided in the *Concurrency exception handling* section of <xref:blazor/tutorials/movie-database-app/part-4>, which is the next article in this tutorial.

:::moniker-end

:::moniker range="< aspnetcore-10.0"
Expand Down
38 changes: 36 additions & 2 deletions aspnetcore/blazor/tutorials/movie-database-app/part-4.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
---
title: Build a Blazor movie database app (Part 4 - Work with a database)
ai-usage: ai-assisted
author: guardrex
description: This part of the Blazor movie database app tutorial explains the database context and directly working with the database's schema and data. Seeding the database with data is also covered.
monikerRange: '>= aspnetcore-8.0'
ms.author: wpickett
ms.date: 11/11/2025
ms.date: 09/15/2026
Comment thread
guardrex marked this conversation as resolved.
uid: blazor/tutorials/movie-database-app/part-4
zone_pivot_groups: tooling
---
Expand Down Expand Up @@ -283,7 +284,7 @@ If the model state has errors when the form is posted, for example if `ReleaseDa

## Concurrency exception handling

Review the `UpdateMovie` method of the `Edit` component (`Components/Pages/MoviePages/Edit.razor`):
Examine the `UpdateMovie` method of the `Edit` component (`Components/Pages/MoviePages/Edit.razor`):

:::moniker range=">= aspnetcore-10.0"

Expand Down Expand Up @@ -313,6 +314,39 @@ private async Task UpdateMovie()
}
```

<!-- UPDATE 11.0 - Delete the following IMPORTANT note and add the
return statement to the example code above after
scaffolder updates go public per
https://github.com/dotnet/Scaffolding/issues/3828.
-->

> [!IMPORTANT]
> Due to a bug in the Blazor CRUD template, a `return` statement is missing from the `UpdateMovie` method after <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A> is called. The purpose of calling `return` is to ensure the handler (the `UpdateMovie` method) selects only the Not Found outcome, independently of a given database provider synchronously or asynchronously executing <xref:Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync%2A>. We're in the process of updating the `Edit` component template, and this article will be updated when the scaffolder generates the correct code.
>
> After the line that calls <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A>, add a `return` statement:
>
> ```csharp
> return;
> ```
>
> The `catch` block should look like the following example after the `return` statement is added:
>
> ```csharp
> catch (DbUpdateConcurrencyException)
> {
> if (!MovieExists(Movie!.Id))
> {
> NavigationManager.NotFound();
>
> return;
> }
> else
> {
> throw;
> }
> }
> ```

Concurrency exceptions are detected when one client deletes the movie and a different client posts changes to the movie.

To test how concurrency is handled by the preceding code:
Expand Down
Loading