How to use 404 routing in Razor Page OnInitialized event
Asked Answered
A

3

16

In a server-side Blazor application (Core 3.1) have a Razor that accepts an identifier in the @page attribute. If the identifier supplied in the URL corresponds to an existing entity, the page will render fine. However, if the identifier is not a known entity (as determined by its presence in the repository) I would like the system to perform whatever action corresponds to 404 Not Found. I don't know this, however, until the route has already been matched and my page's OnInitialized() is executing.

How can I "redirect" to the default 404 handling in this case.

The page looks like this:

@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)
        {
            WANT TO USE 404 ROUTING HERE.
        }
        Project = new UpdateProjectViewModel(project));
    }

}
Araxes answered 24/12, 2019 at 21:21 Comment(3)
inject a NavigationManager by @inject NavigationManager navMgr and navMgr.NavigateTo("/404",false); ?Whimsey
FYI if you go down this route, put it in AfterRender method otherwise exception will be thrown by navimanagerPrimaveria
I'm afraid there isn't anything better than simple if/else in your page.razor file. I do something like this on top of the page: @if(_notFound) { <NotFoundComponent /> return; } . The return statement allows me to skip else block and to prevent nesting in the the rest of the component.Elyot
M
7

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:

  1. Keep you on the same route in the browser
  2. Not navigate to anywhere
  3. 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));
    }
}
Maren answered 17/10, 2021 at 22:48 Comment(5)
Thanks for trying, but I want to display the standard 404 error that is already displayed when a route doesn't match. I don't want to re-invent the 404 page that is displayed in every page I want to do this, nor do I want to convolute the html portion with branching logic.Exemplary
You mean the page that the browser shows? If yes what do you want to see in the address bar?Maren
Updated my answer pls check if that is what you need.Maren
Right, but this still redirects the user. See Ross P's comment in the other answer, "using this approach, would you not end up at "/404"? A "not found response" should not result in a redirect to another page but keep the user at the url where content could not be found."Exemplary
Updated the answer. I have tested it.Maren
C
2

Here is the code snippet

@page "/navigate"
@inject NavigationManager NavigationManager

<h1>Navigate in Code Example</h1>

<button class="btn btn-primary" @onclick="NavigateToCounterComponent">
    Navigate to the Counter component
</button>

@code {
    private void NavigateToCounterComponent()
    {
        NavigationManager.NavigateTo("404");
    }
}
Candis answered 25/12, 2019 at 22:30 Comment(4)
Using this solution, may application believes I'm trying to navigate to a project named 404, which then redirects to 404 and generates a Too Many Redirects error. I assume the "404" string does not have a special meaning and will just be routed according to whatever routes I've set up -- and I need to use something there guaranteed to not be handled by my application?Araxes
You need to place a component in place of 404. Here is the link from where you can get the help: learn.microsoft.com/en-us/aspnet/core/blazor/…Candis
@RafaqatAli, using this approach, would you not end up at "/404"? A "not found response" should not result in a redirect to another page but keep the user at the url where content could not be found.Synchronic
Did you find a solution for this @RossP ? I agree with you, this answer is not the solution and not an "correct" 404.Depreciatory
F
1

My dirty solution is a simple redirect to the not-found page.. which doesn't exist ;)

protected override void OnInitialized()
{
    var project = Repository.Get(ProjectName);
    if (project == null)
    {
        NavigationManager.NavigateTo("/not-found"); 
    }
    Project = new UpdateProjectViewModel(project));
}

This automatically displays the content specified in in app.razor where you can customize its appearance

<NotFound>
        <PageTitle>Not found</PageTitle>
        <LayoutView Layout="@typeof(MainLayout)">
            <p class="alert alert-danger" role="alert">I'm sorry but I couldn't find anything at this address.</p>
        </LayoutView>
    </NotFound>
Footpound answered 4/7, 2023 at 15:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.