dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.3k stars 9.97k forks source link

Support for multiple Blazor Web apps per server project #52216

Open adaris-it opened 10 months ago

adaris-it commented 10 months ago

Is there an existing issue for this?

Is your feature request related to a problem? Please describe the problem.

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

Kumima commented 10 months ago

It seems that there are already plans. #46980

ElderJames commented 10 months ago

I had the same issue, is there a workaround in .NET 8? @SteveSandersonMS

ghost commented 9 months ago

To learn more about what this message means, what to expect next, and how this issue will be handled you can read our Triage Process document. We're moving this issue to the .NET 9 Planning milestone for future evaluation / consideration. Because it's not immediately obvious what is causing this behavior, we would like to keep this around to collect more feedback, which can later help us determine how to handle this. We will re-evaluate this issue, during our next planning meeting(s). If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact work.

AntMaster7 commented 6 months ago

Looking forward to use this ability.

AntMaster7 commented 6 months ago

I had the same issue, is there a workaround in .NET 8? @SteveSandersonMS

@ElderJames The only workaround I know of is simply creating multiple Blazor Web App projects and a separate API project. These projects all need to be hosted separately. But its a huge struggle to get it working if you want the full Blazor experience with auto rendering. You will also run into issues related to authentication. Its possibly and we use this approach. But its no easy feat.

ElderJames commented 6 months ago

@AntMaster7 I have create a demo for that and succeed to host two blazor apps with one entry.

https://github.com/ElderJames/BlazorAutoLayout/tree/master

AntMaster7 commented 6 months ago

@AntMaster7 I have create a demo for that and succeed to host two blazor apps with one entry.

  • endpoint / is blazor static ssr
  • endpoint /Admin is blazor interactive

https://github.com/ElderJames/BlazorAutoLayout/tree/master

Thank you @ElderJames, but I get a 404 error when I want to open that page. Maybe the repository is private?

ElderJames commented 6 months ago

@AntMaster7 I make it public now.

skillcrushbinguyen commented 1 month ago

@AntMaster7 I make it public now.

Hi @ElderJames , I have a repo that has been worked on NET 6 with two Client wasm, do you have any idea when the application has two Client Wasm on NET 8. I already read your repo and I saw your setup worked because in Client project you specified /admin at prefix on every nav link. But in my case it isn't possible, so please help me preview or give me some ideas or keywords to resolve this on NET 8. Thank you!

ElderJames commented 1 month ago

Hello @skillcrushbinguyen , you can try the base path to add the route prefix https://learn.microsoft.com/en-us/aspnet/core/blazor/host-and-deploy/?view=aspnetcore-8.0&tabs=visual-studio#app-base-path