Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use 404 routing in Razor Page OnInitialized event

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));
    }

}
like image 432
Larry Lustig Avatar asked Dec 24 '19 21:12

Larry Lustig


1 Answers

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://docs.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));
    }
}
like image 108
Mayur Ekbote Avatar answered Sep 19 '22 11:09

Mayur Ekbote