Custom Middleware is causing Blazor Server-side page to stop working
Asked Answered
B

3

7

My custom middleware appears to be causing some sort of conflict with my blazor server-side page. The sample middleware in short checks for a bool status and if true redirects to the counter page provided by the out of the box blazor template. Without the middleware inserted into the pipeline the counter page works just fine when you click the button, but once the middleware is placed in the pipeline the button no longer works, as it does not increment the count. I've placed the custom middleware right before the app.UseEndpoints middleware, though it appears it doesn't matter where it's placed, as it doesn't work no matter the order that it's in. Why is my custom middleware breaking the blazor server-side page from functioning properly?

middleware:

class CheckMaintenanceStatusMiddleware
{
    private readonly RequestDelegate _next;
    public CheckMaintenanceStatusMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {


        var statusCheck = true;

        if(statusCheck && context.Request.Path.Value != "/counter")
        {
            context.Response.Redirect("/counter");
        }
        else
        {
            await _next.Invoke(context);
        }
    }

}

public static class CheckMaintenanceStatusMiddlewareExtension
{
    public static IApplicationBuilder UseCheckMaintenanceStatus(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<CheckMaintenanceStatusMiddleware>();
    }
}

configure method in Startup file:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{


    var connectionString = Configuration.GetConnectionString("DefaultConnection");
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }

    app.UseHttpsRedirection();

    app.UseStaticFiles();


    app.UseCookiePolicy();

    app.UseRouting();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseCheckMaintenanceStatus();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
        endpoints.MapBlazorHub();
        endpoints.MapFallbackToPage("/_Host");
    });

}
Boatman answered 19/10, 2019 at 17:22 Comment(7)
What are you trying to achieve with CheckMaintenanceStatusMiddleware?Dishtowel
Ultimately, I would like to have a maintenance page that users will be redirected to, while the site is undergoing maintenance or updates.Boatman
if the site, aka the code gets an update, the middleware wont work, because the site isnt running in the instance of the request, or am i just to confusedHoyle
you understand that this implementation checks every request.. resources like css, js, all of it. and unless I'm mistaken.. it isn't scoped to just this app either.. it is a request chain and handling in and throughout your middleware.. you request a js file on an app in your middle on a different domain, and it will be evaluated here as well.Ossiferous
are you having problems debugging this? you should be able to do a remote debugging instance and add the breakpoint in before you click this button and see what request is being block\redirected... but like I said, it could be a resource that adds the event handler to the button that is blocked on page load and\or routing..Ossiferous
can you give us a screenshot of your network tab in web developer console, all the traffic on the page load to button clickOssiferous
in particular, I'm interested if the redirected page links to a resource that causes a a redirect to the redirected pageOssiferous
R
5

With your code , the blazor negotiate url is also been redirect so negotiate won't work .

Try below codes which avoid that :

if (statusCheck && context.Request.Path.Value != "/counter"&& !context.Request.Path.Value.StartsWith("/_blazor"))
{
    context.Response.Redirect("/counter");
}
else
{
    await _next.Invoke(context);
}
Rilda answered 21/10, 2019 at 6:37 Comment(3)
This code doesn't appear to solve my problem. In fact it doesn't even redirect the page at all now. I'm a bit confused as what's going on here. Where does the "/_blazor" path come from? Why does a simple redirect cause these issues?Boatman
That works in my testing , client will negotiate with server side to determine which protocol the conversation use , it will use /_blazor/negotiate as path .Rilda
Did you use the out of the box blazor server side template with authentication selected? Just want to verify that everything is identical, as I'm not sure why it's working for you and not me.Boatman
S
1

I suspect what's happening here is

  • The browser goes to the app URL and attempts to negotiate a SignalR connection.
  • SignalR returns the protocol and token to use to successfully connect to the Hub (using "/_blazor?id=token_value").

Unfortunately the custom middleware is redirecting every request, so it ends up stopping the application from doing the initial connection to the hub (triggering a bunch of console errors) - though I was able to successfully redirect to "/counter". But because the middleware is stopping the SignalR connection, it breaks the connection from the client to the server which is necessary for Blazor Server apps.

I would suggest moving this check from middleware. You could try creating a service that can return if the app is in maintenance mode, add a service call to the Index.razor component, and then render a "Maintenance Mode" component if necessary.

Syllabism answered 23/10, 2019 at 14:10 Comment(2)
Wouldn't I have to add this service/component to each page in the app though, or am I misunderstanding something? My reason for attempting to use the middleware was obviously to be able to conveniently close the site down, but I do not mind choosing a different strategy. Just want to make sure I clearly understand your recommendation.Boatman
Totally understand. I believe you're right - my original approach would have you making changes in too many pages. The strategy is still correct but instead of doing it in Index.razor, you should do it in MainLayout.razor. This is the parent component for the demo website that actually renders the final HTML displayed in the browser (and changes components based on routes navigated to). You can change the final render div tag rendered with class main on it. This strategy has a hole in it too (if you use different layouts), but not sure if that's a concern hereSyllabism
A
0

This is old post. But I am using app_offline.htm page to stop the blazor app

I put it in the root (content root folder) and have just plain html which says "Site is under maintenance"

This page will terminate all request and all blazor hub connection.

Then, when I am done updating my app, I rename this file to app_offline2.htm

Meaning "app_offline.htm" is a special file name used my framework and Core to determine if any request can be served by application.

Amorita answered 27/1, 2023 at 9:10 Comment(1)
I fail to see how this is answering the question. app_offline is used to suspend user interaction while you update the site not to fix middleware problems.Shoddy

© 2022 - 2024 — McMap. All rights reserved.