dotnet / dotnet/aspnetcore

Support for multiple Blazor Web apps per server project

Open
#52,216 15 comments 21 reactions 0 assignees View on GitHub
area-blazor enhancement feature-application-parts Pillar: Dev Experience Priority:1
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 5h
Merged PRs (30d)
276

Description

### Is there an existing issue for this?

- [X] I have searched the existing issues

### 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()
.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()
.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(ctx => ctx.Request.Host.Port == 7282)
app.MapRazorComponents(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_

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.