Support for multiple Blazor Web apps per server project · Issue #52216 · dotnet/aspnetcore (original) (raw)

Is there an existing issue for this?

Quite often an application consists of several frontend apps which all need to access the same backend API. The new Blazor 8 template in VS assumes only one client app and distributes the razor files involved into 2 projects (the prerendering part is in the server project, the rest is in the client project).

So if I have 3 different frontend apps in Blazor 8, I'd need to have 3x2 = 6 projects, and each server project would essentially duplicate a lot of the shared logic, like authentication etc. Of course I could deploy a 7th project hosting only the common concerns like authentication, but for smaller projects this is overkill and poses new obstacles.

In ASP.NET Core 7 this problem was already solved:
https://learn.microsoft.com/en-us/aspnet/core/blazor/host-and-deploy/multiple-hosted-webassembly?view=aspnetcore-7.0&pivots=port-domain

I tried applying the same principle to Blazor 8, but it always results in an AmbiguousMatchException because both client apps register identical routes using the @page directive (like "/" etc.)

Describe the solution you'd like

The following code should work without any issues since I'm isolating the two clients using a request filter, as proposed in the ASP.NET 7 tutorial.

            app.UseHttpsRedirection();
            app.UseRouting();
            app.UseStaticFiles();
            app.UseAntiforgery();

            app.MapWhen(ctx => ctx.Request.Host.Port == 7282, client1 =>
            {
                client1.Use((ctx, nxt) =>
                {
                    if (!ctx.Request.Path.Value!.Contains("blazor.web.js"))
                    {
                        ctx.Request.Path = "/Client1" + ctx.Request.Path;
                    }
                    return nxt();
                });

                client1.UseStaticFiles();
                client1.UseStaticFiles("/Client1");
                //client1.UseRouting(); //<--- needed???
                client1.UseEndpoints(endpoints =>
                {
                    endpoints.MapRazorComponents<AppClient1>()
                        .AddInteractiveServerRenderMode()
                        .AddInteractiveWebAssemblyRenderMode()
                        .AddAdditionalAssemblies(typeof(Client.Pages.Counter).Assembly);
                });
            });


            app.MapWhen(ctx => ctx.Request.Host.Port == 7283, client2 =>
            {
                client2.Use((ctx, nxt) =>
                {
                    if (!ctx.Request.Path.Value!.Contains("blazor.web.js"))
                    {
                        ctx.Request.Path = "/Client2" + ctx.Request.Path;
                    }                    
                    return nxt();
                });

                client2.UseStaticFiles();
                client2.UseStaticFiles("/Client2");
                //client2.UseRouting(); //<--- needed???
                client2.UseEndpoints(endpoints =>
                {                    
                    endpoints.MapRazorComponents<AppClient2>()
                        .AddInteractiveServerRenderMode()
                        .AddInteractiveWebAssemblyRenderMode()
                        .AddAdditionalAssemblies(typeof(MultiClientTest.SecondClient.Pages.Counter).Assembly);
                });
            });
            
            app.Run();

Alternatively you could offer an optional builtin feature for isolating multiple client apps, like so:

app.MapRazorComponents<AppClient1>(ctx => ctx.Request.Host.Port == 7282)
app.MapRazorComponents<AppClient2>(ctx => ctx.Request.Host.Port == 7283)

I'm aware that this could still lead to ambiguous route mappings if the request filters aren't mutually exclusive; but then the framework would just throw an exception as it already does today.

Additional context

No response