In case you want to "remain" on the same route while showing error 404:
Create a class NotFoundListener.cs
public class NotFoundListener
{
public Action OnNotFound { get;set; }
public void NotifyNotFound()
{
if(NotifyNotFound != null)
{
OnNotFound.Invoke();
}
}
}
Inject it as a scoped service
builder.Services.AddScoped<NotFoundListener>();
In your MainLayout.razor
@inherits LayoutComponentBase
@inject NotFoundListener nfl;
<PageTitle>ImportTesting</PageTitle>
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4">
<a href="https://learn.microsoft.com/aspnet/" target="_blank">About</a>
</div>
<article class="content px-4">
@if (notFound)
{
<h1>Could not find the content you are looking for</h1>
}else
{
@Body
}
</article>
</main>
</div>
@code{
private bool notFound;
protected override void OnInitialized() => nfl.OnNotFound += SetNotFound;
void SetNotFound()
{
notFound = true;
StateHasChanged();
}
}
And in the page you want to raise 404:
protected override void OnInitialized()
{
if (project == null)
{
nfl.NotifyNotFound();
}
}
This will:
- Keep you on the same route in the browser
- Not navigate to anywhere
- Ensure no if else on every page
(I have used Action for event handling. It's not the best way to use it, but makes the code simpler to read)
Now,
- You can have different event listeners for different pages.
- You can create different layouts depending on your exact need.
- If you want to apply this only on certain pages but not all, then add a
'route' argument to the event and inspect it on the MainLayout.
In case you want to re-use your standard error page:
You error page is defined in your App.razor
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
You can create your own NotFoundComponent.razor
component
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
Your updated App.razor
looks like this:
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<NotFoundComponent/>
</NotFound>
</Router>
And then you can create a page that simply refers to the same component
NotFoundPage.razor
@page "/NotFound"
<NotFoundComponent />
And then use your page redirection as below
From your OnInitialized()
@page "/{projectname}"
<!-- HTML Here -->
@code {
[Parameter]
public string ProjectName {get; set;}
private UpdateProjectViewModel Project;
protected override void OnInitialized()
{
var project = Repository.Get(ProjectName);
if (project == null)
{
NavigationManager.NavigateTo("/NotFound");
}
Project = new UpdateProjectViewModel(project));
}
}
@inject NavigationManager navMgr
andnavMgr.NavigateTo("/404",false);
? – Whimsey@if(_notFound) { <NotFoundComponent /> return; }
. The return statement allows me to skipelse
block and to prevent nesting in the the rest of the component. – Elyot