George Fabish
George Fabish

Reputation: 439

Redirect from Blazor Component(.razor) to Razor Page(.cshtml) [ServerSide]

Here's my use case. I have a singleton object that is holding global site state. I have a button on my website that when clicked fires an event on the global site state object. I have a Blazor component(razor) that is listening for this event. It responds to the event by doing this

var path = "siteupdate".AddInitialQueryStringArg("returnUrl", _navManager.Uri);
_navManager.NavigateTo(path,true);

The idea is that all online users are redirected to a Razor page(.cshtml) that has a message saying "The Website is being updated, come back later". This is a requirement to provide a decent user experience for when the server goes down temporarily for an update and loses connection to the SignalR hub.

Following the execution it appears as if the navigator tries to navigate to the siteupdate page but it ends up getting rejected and resets back to whatever component I was on when I make the call. Similar to when you try to update the url in the browser manually and the navmanager resets it. Putting breakpoints in my Razor Page I can tell that the OnGet method gets called most of the time, but it seems like it's response is not being acknowledge or something? I've tried the following :

Attempted to set window location via javascript


var path = "/update".AddInitialQueryStringArg("returnUrl", _navManager.Uri);
JSRuntime.InvokeVoidAsync("BlazorHelpers.RedirectTo", path);

Attempted to not use force reload, this results in a 404

var path = "/siteupdate".AddInitialQueryStringArg("returnUrl", _navManager.Uri);
_navManager.NavigateTo(path);

For reference I have the Razor page under Pages/SiteUpdate/UpdateLandingPage.cshtml I tried placing it just under the Pages folder, but like I said earlier the OnGet method is getting called so the router seems to be finding it, but it seems like the framework is overwriting the URL somehow.

Thanks

Upvotes: 4

Views: 2928

Answers (1)

Quango
Quango

Reputation: 13468

You have two parts to your problem: a common update service that all users see, and the individual page a user is on when the update occurs which sets the redirect. These would need Singleton and Scoped contexts respectively.

I tested this out creating two services:

UpdateService.cs
    /// <summary>
    /// A singleton service that alerts users about updates
    /// </summary>
    public class UpdateService
    {
        /// <summary>
        /// Event raised when an update starts
        /// </summary>
        public event Action OnUpdate;

        public void TriggerUpdate()
        {
            // trigger the event
            OnUpdate?.Invoke();
        }
    }

This has an event OnUpdate that consumers can subscribe to when the update occurs.

RedirectService.cs
    /// <summary>
    /// Redirect service (user-scoped)
    /// </summary>
    public class RedirectService : IDisposable
    {
        private readonly NavigationManager navigationManager;
        private readonly UpdateService updateService;

        public RedirectService(NavigationManager navigationManager, 
                               UpdateService updateService)
        {
            this.navigationManager = navigationManager;
            this.updateService = updateService;
            // register for updates
            this.updateService.OnUpdate += HandleUpdate;
        }

        private void HandleUpdate()
        {
            // get user's current URL
            var uri = new Uri(navigationManager.Uri);

            if (!uri.PathAndQuery.StartsWith("/siteupdate", StringComparison.CurrentCultureIgnoreCase))
            {
                // redirect
                navigationManager.NavigateTo($"/siteupdate?redirectUrl={uri.PathAndQuery}");
            }
        }

        // Cleanup
        public void Dispose()
        {
            updateService.OnUpdate -= HandleUpdate;
        }
    }

This service consumes the UpdateService and listens for OnUpdate events. If the current URL isn't already "/siteupdate" it redirects the user.

In Startup.cs you register these two services

// a singleton service - all users share this
services.AddSingleton<UpdateService>();
// scoped service for the redirect
services.AddScoped<RedirectService>();

The order is important since RedirectService needs the UpdateService.

To get this to trigger I just injected the service in MainLayout.razor:

@inherits LayoutComponentBase
@inject Data.RedirectService redirectService

To test this I put a button on Index.razor and when clicked that user and all other users on different pages/browsers all redirected to the new page.

That means that any user on any page except /siteupdate will be redirected. If that's not your use-case (e.g. only some pages/users should redirect) you can adapt accordingly.

Upvotes: 1

Related Questions