Open javiercn opened 2 years ago
This is also important for micro-frontend scenarios where one might have multiple "islands" of Blazor on the same page. For example, we're considering adopting single-spa to modernize our existing monolithic front-end into smaller micro-frontends and it would be great if there was a way to build some of these micro-frontends using Blazor.
hello, as one of way, after download wasm binaries swap server to wasm https://github.com/jdtcn/HybridBlazor
and second for state machine between server and client using this case https://github.com/servicetitan/Stl.Fusion
Current work around. Currently i am able to mix them up but instead have to use MVC controller & view on the Blazor Server. So i create special end point which will render content from MVC controller on Blazor Server. But it would be nice if i could instead use Razor page in place of MVC controllers & views.
I had couple of scenarios 1) I send SMS Link. when user clicks that link , it will hit MVC controller & view which renders QR Code. In this scenario i don't want user to load my entire Blazor wasm app, because he just need to display QRCode only on his mobile device, which will be scanned from Blazor wasm app . For this simple QrCode Display, I also did not want to spin up separate Blazor Server project and Host it, hence used MVC controller & view from within Blazor Server.
2) I have some logic(financial calculation) which I don't want to be exposed on the client side and for this logic, i just need to render HTML output. For this again, i was able to invoke the MVC Controller & return view from the controller from the Blazor Server Side, but this loads the page separately out side of Blazor Wasm. In this case i would like to have it render the server content in my Blazor wasm page which is not possible for now.
We are developing a large system (a consumer application) having multiple modules using the same user interface. The user can switch between modules through the user interface. The user interface project (that we call the shell) provides services such as menus, UI notifications etc... that should are consumed by module projects.
With this architecture, our goal is to ensure that a developer only has access to the source code repository related to the module he is developing (instead of having full access to the source code of a monolith) because there will be a large number of modules, some of them developed for sensitive customers. The goal also is to enable us to treat every module as a micro-service and deploy it to a difference server.
Blazor Server is exactly what we need (we love the concept, it is like having a full state desktop app running in the web for each user and this enables so many opportunities), but we are attempted to go the monolith way because we struggle to figure out a development workflow that would be practical with Blazor Server. The monolith approach would cause issues with both intellectual property protection, but also with the problems related to restarting a stateful Blazor Server applications whenever an update in one of the many modules will be required (and hereby disconnecting a very large number of users).
Being able to load a module in an Iframe-like container in the shell, and why not have some sort of built-in interprocess communication facility between the shell and the loaded modules (for example to update the menu, or to enable the modules to call UI notification services), would be great. We currently need to reference and deploy the shell project with every module (and therefore reload the user interface whenever the user moves across modules).
能在支持多个Area吗
Thanks for contacting us.
We're moving this issue to the .NET 7 Planning
milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. 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 issues.
To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.
Will this also allow combining Blazor Server and razor pages applications?
@mrlife no, this is different.
You can already render multiple Blazor Server components in a Razor Pages application.
@javiercn Thanks for letting me know. It's a little different... it's about combining 2 different apps that run at the same URL, e.g. example.com and example.com/app2, where the former is razor pages project and the latter (app2) is a separate Blazor project.
it's about combining 2 different apps that run at the same URL
That's not something we are planning to invest on.
For me it is important to inject blazor app as WASM component to HTML page, and in the nature of things, a HTML page can contain serveral WASM components from different language (go, rust, java, c++, .net).
This is also useful for cases where I want to mix a blazor server app in with wasm app or run a server app but have a wasm graphics library
A practical example is running skia in the browser. But this could be a 3D engine too. I want the full app to be a server app because of reasons, but the rendering logic needs to happen on the client as it is part of webgl and in a render loop. I am expecting to send batches of drawing commands to the client and then the client must update and handle cases like panning and zooming without requiring server interactions.
A more concrete example is maybe an image editing app. The server needs to handle all the page, auth, feature sets, storage and the client just makes editing happen. For example, the user could load a project and that all gets downloaded to the server one time. The user can pan, zoom, crop, recolor. Then hitting the save or export, the new state gets sent to the server for rendering and persistence.
In fact, think of me treating the wasm part as a js library, but written in c++ and .net.
Interactions between the server and client would be on the basis of either sending json or byte arrays via a method call... Or somehow. And then the client would parse or deserialize it. Just like I would if I was doing a web API, but more magical.
Yep, that's similar to my use case; server based code for the majority of the work, but a wasm component that runs entirely client-side that allows the user to modify images (changing hue, saturation, etc) without requiring the image to refresh server side.
This is also useful for cases where I want to mix a blazor server app in with wasm app or run a server app but have a wasm graphics library
A practical example is running skia in the browser. But this could be a 3D engine too. I want the full app to be a server app because of reasons, but the rendering logic needs to happen on the client as it is part of webgl and in a render loop. I am expecting to send batches of drawing commands to the client and then the client must update and handle cases like panning and zooming without requiring server interactions.
A more concrete example is maybe an image editing app. The server needs to handle all the page, auth, feature sets, storage and the client just makes editing happen. For example, the user could load a project and that all gets downloaded to the server one time. The user can pan, zoom, crop, recolor. Then hitting the save or export, the new state gets sent to the server for rendering and persistence.
In fact, think of me treating the wasm part as a js library, but written in c++ and .net.
Interactions between the server and client would be on the basis of either sending json or byte arrays via a method call... Or somehow. And then the client would parse or deserialize it. Just like I would if I was doing a web API, but more magical.
Thanks @mattleibow, this is what we'd be looking to do. Some other minor requirements from our side based on your great foundational description (and perhaps these are unrelated):
For anyone else subscribed to this issue and awaiting it after the discussion of it earlier in the year, it's been moved to a .Net 8 milestone. Commenting here explicitly because that move/postponement doesn't result in a notification.
This is also important for micro-frontend scenarios where one might have multiple "islands" of Blazor on the same page. For example, we're considering adopting single-spa to modernize our existing monolithic front-end into smaller micro-frontends and it would be great if there was a way to build some of these micro-frontends using Blazor.
As I mentioned earlier we're looking to single-spa as a way to modernize our current monolithic frontend by using a micro frontend approach so that each team can build and maintain their own features independently of each other. Obviously we're also looking at Blazor to build some of these micro frontends in the future. I recently had the chance to do some experiments with running a Blazor WebAssembly app in single-spa. While I was able to get something running in the end, I did have some observations that I thought were worth sharing here:
#app
. This can be changed in the Program.cs
of course, but will need to be made dynamic in order to work with single-spa. Unfortunately there doesn't seem to way to pass arguments to the Main()
from the JavaScript Blazor.start()
function currently (see #24461), so I passed it in as the environment instead but that's less than idealBlazor.start()
function, but not a Blazor.stop()
function. This creates problems with single-spa, because different SPA applications might use the same DOM element to render, which is implemented through a mount
and unmount
system. So while the Blazor app will start just fine the first time, it will not restart if you navigate back to it later on.I've also had a look at the new .NET 7 features where you could use .NET from any JavaScript application. That seems to provide at least some of the things described above, so could but a better fit. But as soon as I try to use Blazor with that things start to break apart as that requires blazor.webassembly.js
to be loaded which probably sets up a few things in the JavaScript world for Blazor. That does however beg the question of whether we might see Blazor being "replatformed" on top of the low-level API's that are now available in .NET 7?
In our scenario, there are several manufacturers of web components who also want to host them separately and (partially) use Blazor. Embedded in only one web application / page. Here it is necessary that the individual Blazor WebComponents are independent of each other (hosts, versions, and so on). As described in #44588, a complete encapsulation in WebComponents is required. Therefore, the integration of Blazor, for this use, must be done within the WebComponent Islands.
Sketch / Example:
Host html-document is from manufacturer A
<script type="module" src="https://vendor-b/path/to-module-file.js"></script>
<script type="module" src="https://vendor-c/path/to-module-file.js"></script>
[...]
<component-from-vendor-b>Blazor V8.0</component-from-vendor-b>
<component-from-vendor-c>Blazor V9.0</component-from-vendor-c>
That does however beg the question of whether we might see Blazor being "replatformed" on top of the low-level API's that are now available in .NET 7?
@jmezach We weren't able to get this done for .NET 7, but we do plan to do this for .NET 8.
CustomElement wouldn't work in a Blazor app, if it's hosted as a seperate app (on same site). Same basic issue yes? Also using CustomElement in MVC on same host is complicated (is it possible?). (Steve's conf Wordpress talk didn't show specifics on what doesn't work)
Probably already mentioned but this would be great for issues like #27592 where an initial page hit runs server side but then once all wasm components are downloaded it has the option to run client side. Not sure if tis possible but might help with issues with auth integration and eliminate separate Host server app maybe.
For those that haven't seen it, this is likely of interest and is relevant to this issue: https://www.youtube.com/watch?v=48G_CEGXZZM (cc @SteveSandersonMS)
This is also important for micro-frontend scenarios where one might have multiple "islands" of Blazor on the same page. For example, we're considering adopting single-spa to modernize our existing monolithic front-end into smaller micro-frontends and it would be great if there was a way to build some of these micro-frontends using Blazor.
We have done a PoC, too, about micro frontends with Blazor. This feature is very important for the future of Blazor on our side. Will the feature definitely be a part of the .NET 8 release?
@thomashauser this is scheduled for .NET 8.0 and is part of our roadmap.
Unless we discover something, we think this will be part of .NET 8.0. That's said, many things can still happen until the release is out, so take my words with a grain of salt.
很关注这个功能,到底什么时候能解决呢,希望能实现
already in net8 preview there is blazor united
Unfortunately, again not what one would have expected from an architectural point of view. As already clearly stated in other comments, it is about the possibility to use microfrontends in Blazor. Since it is apparently not clear what is meant by this, here is a list of requirements:
We have various micro services, all running in separate Azure Web Apps with their own addresses. They are developed and deployed by different teams in different repositories with different pipelines.
The services provide components that are used in different apps. For example, favorites can be added in all apps. And this should always look identical, of course. The header bar has a basic appearance and the contents differ in the end depending on the app.
Since services are subject to different development cycles, the Blazor version can of course also differ.
Blazor united does not seem to be the answer to all these requirements. Although here on this page was also repeatedly described by microfrontends, this requirement seems not to have been understood. Blazor united is good for applications that are again in danger of becoming a monolith. Blazor united will certainly have a positive impact on start-up performance. But Blazor united does not help me one step further regarding microfrontends.
If I have misunderstood anything, let me know.
I'll chime in and say I'm very interested in this being a supported use case for Blazor. This has been hashed a few times already, but to put my spin on it: we would like the ability to load and run multiple, independently built and independently released Blazor WASM apps on the same page. In theory each app could be mounted to different parts of the DOM, or they could mount and unmount from a common mount node in the DOM based on the current client-side route.
The latter scenario is one I have been actively investigating off and on over the past year. In that case I use single-spa to lazily load and mount microfrontends (MFEs) in our app based on the current client-side route. Ideally in my case I would support traditional JS-based MFEs as well as Blazor-based ones.
In my learnings, one of the challenges in supporting Blazor MFEs is the reliance on global Blazor and DotNet objects and global navigation event handlers attached to the window by the Blazor startup script. There's also the aspect that a Blazor-based MFE's resources would typically load from an origin that differs from the origin of the page loading and running the MFEs.
Last year I hacked together a set of patches that I could apply to the .NET 6 version of the aspnetcore repo and build a custom blazor.webassembly.js script that gave enough control over managing global Blazor/DotNet objects, nav event handlers, and the resource URIs used to fetch the resources for a Blazor app. This allowed me to make Blazor-based MFEs to work, but there were some caveats and restrictions that one had to know about lest you shot yourself in the foot.
If MFEs were a properly supported use case for Blazor, my hope would be that not only would these foot guns be a non-issue, but it would also mean I wouldn't have to internally maintain a set of patches for my custom Blazor WASM startup script that makes this even feasible in my end.
Michael , did you achieve this via changing hosting ports for each micro in the respective.js file ?
From: Michael Romer @.> Sent: Thursday, April 13, 2023 3:43:02 PM To: dotnet/aspnetcore @.> Cc: Josh Miller @.>; Comment @.> Subject: Re: [dotnet/aspnetcore] Ability to run multiple Blazor server / Web assembly apps in the same document (Issue #38128)
I'll chime in and say I'm very interested in this being a supported use case for Blazor. This has been hashed a few times already, but to put my spin on it: we would like the ability to load and run multiple, independently built and independently released Blazor WASM apps on the same page. In theory each app could be mounted to different parts of the DOM, or they could mount and unmount from a common mount node in the DOM based on the current client-side route.
The latter scenario is one I have been actively investigating off and on over the past year. In that case I use single-spahttps://single-spa.js.org/ to lazily load and mount microfrontends (MFEs) in our app based on the current client-side route. Ideally in my case I would support traditional JS-based MFEs as well as Blazor-based ones.
In my learnings, one of the challenges in supporting Blazor MFEs is the reliance on global Blazor and DotNet objects and global navigation event handlers attached to the window by the Blazor startup script. There's also the aspect that a Blazor-based MFE's resources would typically load from an origin that differs from the origin of the page loading and running the MFEs.
Last year I hacked together a set of patches that I could apply to the .NET 6 version of the aspnetcore repo and build a custom blazor.webassembly.js script that gave enough control over managing global Blazor/DotNet objects, nav event handlers, and the resource URIs used to fetch the resources for a Blazor app. This allowed me to make Blazor-based MFEs to work, but there were some caveats and restrictions that one had to know about lest you shot yourself in the foot.
If MFEs were a properly supported use case for Blazor, my hope would be that not only would these foot guns be a non-issue, but it would also mean I wouldn't have to internally maintain a set of patches for my custom Blazor WASM startup script that makes this even feasible in my end.
— Reply to this email directly, view it on GitHubhttps://github.com/dotnet/aspnetcore/issues/38128#issuecomment-1507644530, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AWYSXXFPICZU7UV6U3ZX4JLXBBXONANCNFSM5HOJAXOA. You are receiving this because you commented.Message ID: @.***>
@joshmiller1FEI , no, at least in the working proof of concept I put together, I did not have to use alternate ports. In my case, the Blazor MFEs were Blazor WASM projects that I prepared via dotnet publish
. I then uploaded each MFE's static assets (JS, WASM, CSS, etc.) to a separate location within a publicly accessible Azure Storage account. Then, I configured single-spa to just load each MFE's assets from its respective location in that storage account. At no point did I have to try loading assets from an alternate port.
The crux of what I had to do was to add a number of properties to WebAssemblyStartOptions that let me independently configure things like the base URI Blazor/.NET would use for fetching resources and the base URI Blazor uses for its internal navigation and routing purposes. These options would get set when Blazor.start
gets called inside things like the single-spa bootstrap/mount lifecycles for an MFE. Most of the changes were then around plumbing these values to the right spots in the Blazor WASM JS glue code where it was instead just using things like document.baseURI
(again, not valid if MFEs are loaded from different origins or if the MFE's base client-side URI doesn't match the document's base URI).
There were some additional methods I added to IBlazor to manage when a particular Blazor app's nav event handlers were active. Essentially, they allowed for adding the handlers when single-spa mounts the Blazor MFE and removing them when the MFE is unmounted. This hack was basically to work around the limitation brought up by @jmezach above in his second point about Blazor not having the ability to start/stop/pause the app during these key lifecycle events.
I did this all a year ago, so there may have been changes made to Blazor either later in .NET 6 or as part of .NET 7 that make some of this easier. Though judging by the discussion above, I doubt it, so I've started picking this back up again. This was/is all done as part of an ongoing project at work, so while it has been my intention to get a working demo publicly on GitHub and even though there's nothing proprietary in the work I've done, I would need to clear things with my company's legal department first.
@mvromer would def be interested in seeing the code that you put together to make this work.
Reiterating and emphasizing what has already been written by @thomashauser... I integrated in a "shell" (frame/"master page"/"microfrontemd"...) Blazor Server a series of web components (also implemented in Blazor server) provided by different ASP.NET Core web apps, and running on different servers (or Pods on AKS). This to support a logic of separation of responsibilities between different teams, and also to distribute the "server" load among multiple processing nodes (or Pods). I used .NET 7. But to do this I had to implement a sort of "trick"... For example, consider the need to "visualize" (with a kind of "Microfrontend" implementation...) in a "Shell" Web App "ClnBlz" a server Web Component "Counter" exposed (with the RegisterCustomElement) as "my-blazor-counter1", hosted by a server ASP.NET Core Blazor Server Web App "Blazor1"; so the MF web app "ClnBlz" is hosted on a different server/Pod than the "Blazor1" "component" app. In addition to the canonical registration as a web component on the "Blazor1" web app side, I have also implemented these two changes in the web component "Blazor1" web app:
In Program.cs I have registered the "server" SignalR Hub with the statement:
app.MapBlazorHub("/_custom1");
In _Host.cshtm I started the Blazor Framework with the statements:
<script> Blazor.start({ configureSignalR: builder => { builder.withUrl("_custom1"); } }); </script>
Ad here's what I implemented on the "consumer" / frame / shell / MF Web App "ClnBlz" side (the "microfrontend portal"):
<iframe id="server1" name="server1" src="blazor1.html" scrolling="yes"></iframe>
<my-blazor-counter1></my-blazor-counter1>
followed by a "custom load" of the Bazor server javascript library:
"_framework_custom1/_framework/blazor.server.js"
with endpoint withUrl "_custom1".
Note the "_flamework_custom1" path prefix... In fact, the microfrontend web app "ClnBlz" contain an implementation of a local proxy (for example YARP, https://microsoft.github.io/reverse-proxy/) able (with a set of specified RULES based on paths) to "re-route" the requests to Blazor server to the "right" server (that in this case is not the "local" one hosted by "ClnBlz", but the one hosted by the DIFFERENT web component web app "Blazor1"...).
I know this is a set of "tricks"... and hopefully with .NET 8 I hope that it will support the "native" integration into one Blazor server web app of multiple other Blazor server (web) components, provided by several and additional Blazor server web apps.
Recently I did some further tests, with a more "simple" approach than the one described above.
In summary, I have implemented some pages as ASP.NET Core Blazor Server Web App pages; call one of these pages here "PageB1", as part of an ASP.NET Core Blazor Server Web App "B", and I hosted this page in an IFRAME in another different ASP.NET Core Blazor Server Web App called here "A", specifically in a page that we can call "PageAShell". So, the "PageAShell" of the ASP.NET Core Blazor Server web app "A" can show in a simple IFRAME the PageB1 exposed by the different ASP.NET Core Blazor Server Web App B (hosted in a different server and process). With some configuration I'm also able to send events from the page PageAShell to the page PageB1, and vice versa. I do not have used in this implementation special configuration as described before in the first part of this post.
Overall this simple setup looks promising (it just works...), but there is an issue related to the SSO (Single-Sign On) when the web apps involved are secured on Azure AD (Entra). Both the ASP.NET Core Blazor Server web apps involved (A and B) are registered (in my case) in Azure AD (Entra), specifically with the same App Registration (because are "de facto" part of a single "platform" in business terms). And both the web apps A and B use the Azure AD OpenID Connect authentication mechanism, as officially supported and described by Microsoft. But when the page PageB1 of the second Web App (B) is "opened" in the IFRAME from the Web app A page PageAShell, there is the well known situation related to the X-FRAME OPTIONS SET TO DENY (with the related login error appearing in the IFRAME). At the moment the "first workaround" that I've found (decidedly crude to be honest) is to open (temporarily) in Javascript from the web app A a window (with a simple window.open()) in a popup (so outside the IFRAME) with the url of the PageB1 (or any other "authenticated"/"secure" page exposed by the Web App B...) in Web App B. This approach enable an authentication roundtrip/refresh in the Web App B (flow that is "silent" from the user point of view...), and - later - an hosting in an IFRAME in PageAShell of the PageB1 works well, with the full (good) SSO experience. To support at the best this appoach I have also did an override of the OIDC authentication sequence in Web App B defining an
options.Events = new OpenIdConnectEvents { OnRedirectToIdentityProvider = async ctxt => ... }
to be able to pass to Web App B a couple of previously valued (by Web App A) cookies with the IdTokenHint and LoginHint, so that I can set this hint in the loading of Web App B OnRedirectToIdentityProvider event:
context.ProtocolMessage.IdTokenHint = token; ... context.ProtocolMessage.LoginHint = name; . Passing the hints (by cookies in my case) from the Web App A to the Web App B and set these hints in the OnRedirectToIdentityProvider of the Web App B increases the chances of an effective (AND "SILENT"!) SSO - without any user interaction - on the web app B.
The main problem of this approach is that from the user point of view there is a temporary open (and close) of an authentication popup window, and only later the user can see (in SSO) the PageB1 correctly hosted in the IFRAME of the PageAShell page.
It would be interesting to define a "cleaner" approach to implement the same experience, without the need to implement a "temporary" opening of a popup window aimed only to support the right SSO of web app B in the context already defined by Web App A; this could perhaps be implemented using an ad-hoc implementation based on MSAL.js ssoSilent (in Web App B probably), or based on the invocation by the web app A (or B...) of a login API exposed by Azure AD (calling something like "https://login.microsoftonline.com/common/oauth2/v2.0/authorize..."). So, the solution I experimented with the window.open (popup) approach seems to work, but the user experience is actually "dirtied" by this temporary popup, which would be best avoided (obviously).
What would be interesting could be an indication or guideline by Microsoft on how to implement a pattern of this type (SSO of a Entra secured Blazor server web app hosted in an IFrame in another Entra secured Blazor server web app) using a SSO authentication based on Azure AD (Entra) APIs calls and/or on a specific MSAL.JS ssoSilent adoption, tailored to this specific context. All withouth (hopefully) the need to use a temporary window open/close.
The investigation continues... share your impressions and experiences on the matter... ;-)
PS: Personally I believe that if it will be possible to manage the SSO problem (as described in the terms I summarized above), then the possibility of integrating (also via IFRAME) multiple Blazor Server "microfrontends" in an ASP.NET Core Blazor Server shell will be correctly and completely addressed.
We've opened #48032 to track the ability to have a single Blazor app that uses Server and WebAssembly components on the same page. This enables scenarios critical to the full-stack Blazor effort. It does not, however, include the ability to have completely separate Blazor app instances exist on the same page. That remaining work is what this issue tracks.
@mkArtakMSFT 请问这个问题到底什么时候能解决
I managed to get approval from my employer to upload the patches I've made for creating a custom Blazor WebAssembly start script. Late last week I cleaned them up and got them to cleanly apply to latest versions of .NET 6 and .NET 7 tagged in the dotnet/aspnetcore repository (so v6.0.16 and v7.0.5, respectively, at the time of writing).
The patches along with scripts that apply them are on GitHub located at mvromer/Blazor.WebAssembly.SingleSpa. I also setup a GitHub workflow to build a NuGet that includes the patched versions of blazor.webassembly.js
for both .NET 6 and .NET 7 as well as corresponding MSBuild props files that ensure the patched script is included in the Blazor WebAssembly build process. The NuGet package is also named Blazor.WebAssembly.SingleSpa. Installing the package in a Blazor WASM project should be sufficient for picking up the patched script. If you do nothing else, by default the patched script should preserve the behavior of the original script.
This script really only exposes just enough hooks for managing the load and lifecycle of a Blazor WASM application. It doesn't do anything related to defining any of the single-spa lifecycle callbacks a micro frontend has to implement. How you do that really depends on your project's requirements.
That being said, the last couple of days I built and deployed a demo application that uses single-spa to integrate both a Blazor-based micro frontend (using the above NuGet) and a Lit (Web Component)-based micro frontend. It's really just a proof of concept as opposed to showing anything necessarily production-grade. That said, it does show how you can use Blazor's router for internal navigation when the Blazor MFE is mounted as well as how to dynamically load CSS styles provided by the Blazor MFE on top of what the app shell provides.
The demo repository is located at mvromer/blazing-lit-mfe-demo. I've also deployed the app shell and the two micro frontends each to their own free instance of Azure Static Web Apps so others can see what this would look like "for reals". The live instance demo app can be accessed at https://gentle-river-0e21e6610.3.azurestaticapps.net/.
Hopefully this can spur some sort of discussion around more officially supporting the micro frontend use case with Blazor. This would be really beneficial in my case where roughly half my team is well-versed in .NET and the other half is well-versed in JavaScript/TypeScript. They're all building independent modules (micro frontends) that will eventually plug into a single portal/web app built on single-spa. As the above shows, it's certainly possible to pull this off for the Blazor-based modules, but it's also not the most ideal way of supporting this in the long run IMO.
We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.
Hello everyone. I just wanted to drop a comment to explain the reason for moving this issue to the backlog. This issue wasn't super clear from the beginning as it covered two separate asks:
The #1
above is very much aligned with supporting the Full Stack Web Development with Blazor
work that we're doing in .NET 8, hence we've prioritized completing that work in .NET 8 as part of https://github.com/dotnet/aspnetcore/issues/48032. There is a little bit of work left to complete it, which is tracked by https://github.com/dotnet/aspnetcore/issues/48396
This particular issue we will use for tracking support for the #2
scenario. That will require more work which we won't be able to tackle in .NET 8 timeframe. Hence, I've moved it to the Backlog so that we revisit it after .NET 8.
The Microfrontents use-case is a very interesting one. Right now, one would develop the components as Razor Libraries, and reference them together on the hosting application. But any update to the component libraries would need to trigger a rebuild of the host, with multiple different components each going their own pace, that leads to a lot of fragmentation, even if they are totally separate.
If each component library was hosted individually, and loaded by the browser at runtime, this would untangle a lot of issues ... But also create a few more ... What about shared assemblies (libraries)? With one component already upgraded to ASP.NET 7.0.10, another still at 7.0.8 ... the browser should be able to know what assembly to load, similar to how nuget resolves versions.
If they are isolated, they are both loaded in memory per library service, if they are shared, everything needs to be eager-loaded. Because when it is lazy-loaded, the order in which dependencies are loaded can potentially reverse. Loading first 7.0.8 and then requesting to load 7.0.10 won't work?
I hope this also includes the ability to have multiple, independent projects to be able to contribute HTML custom elements to a single page. Blazor HTML custom elements currently have a rather limited use case because there can only be one source.
希望能尽早完善解决这个问题
### An Update (Dec 8, 2023) to my previous post dated May 8, 2023:
In the context of an ASP.NET Core Blazor Server web app able to "host" (in this case by IFRAME) some pages/components exposed by another different ASP.NET Core Blazor Server Web App, recently I did some further tests, with a more "simple" approach than the one described in my previous post.
In summary, I have implemented some pages as ASP.NET Core Blazor Server Web App pages; call one of these pages here "PageB1", as part of an ASP.NET Core Blazor Server Web App "B", and I hosted this page in an IFRAME in another different ASP.NET Core Blazor Server Web App called here "A", specifically in a page that we can call "PageAShell". So, the "PageAShell" of the ASP.NET Core Blazor Server web app "A" can show in a simple IFRAME the PageB1 exposed by the different ASP.NET Core Blazor Server Web App B (hosted in a different server and process). With some configuration I'm also able to send events from the page PageAShell to the page PageB1, and vice versa. I do not have used in this implementation special configuration as described before in my previous post.
Overall, this simple setup looks promising (it just works...), but there is an issue related to the SSO (Single-Sign On) when the web apps involved are secured on Azure AD (Entra). Both the ASP.NET Core Blazor Server web apps involved (A and B) are registered (in my case) in Azure AD (Entra), specifically with the same App Registration (because are "de facto" part of a single "platform" in business terms). And both the web apps A and B use the Azure AD OpenID Connect authentication mechanism, as officially supported and described by Microsoft. But when the page PageB1 of the second Web App (B) is "opened" in the IFRAME from the Web app A page PageAShell, there is the well known situation related to the X-FRAME OPTIONS SET TO DENY (with the related login error appearing in the IFRAME). At the moment the "first workaround" that I've found (decidedly crude to be honest) is to open (temporarily) in Javascript from the web app A a window (with a simple window.open()) in a popup (so outside the IFRAME) with the url of the PageB1 (or any other "authenticated"/"secure" page exposed by the Web App B...) in Web App B. This approach enable an authentication roundtrip/refresh in the Web App B (flow that is "silent" from the user point of view...), and - later - an hosting in an IFRAME in PageAShell of the PageB1 works well, with the full (good) SSO experience. To support at the best this appoach I have also did an override of the OIDC authentication sequence in Web App B defining an
options.Events = new OpenIdConnectEvents { OnRedirectToIdentityProvider = async ctxt => ... }
to be able to pass to Web App B a couple of previously valued (by Web App A) cookies with the IdTokenHint and LoginHint, so that I can set this hint in the loading of Web App B OnRedirectToIdentityProvider event:
context.ProtocolMessage.IdTokenHint = token; ... context.ProtocolMessage.LoginHint = name; . Passing the hints (by cookies in my case) from the Web App A to the Web App B and set these hints in the OnRedirectToIdentityProvider of the Web App B increases the chances of an effective (AND "SILENT"!) SSO - without any user interaction - on the web app B.
The main problem of this approach is that from the user point of view there is a temporary open (and close) of an authentication popup window, and only later the user can see (in SSO) the PageB1 correctly hosted in the IFRAME of the PageAShell page.
It would be interesting to define a "cleaner" approach to implement the same experience, without the need to implement a "temporary" opening of a popup window aimed only to support the right SSO of web app B in the context already defined by Web App A; this could perhaps be implemented using an ad-hoc implementation based on MSAL.js ssoSilent (in Web App B probably), or based on the invocation by the web app A (or B...) of a login API exposed by Azure AD (calling something like "https://login.microsoftonline.com/common/oauth2/v2.0/authorize..."). So, the solution I experimented with the window.open (popup) approach seems to work, but the user experience is actually "dirtied" by this temporary popup, which would be best avoided (obviously).
What would be interesting could be an indication or guideline by Microsoft on how to implement a pattern of this type (SSO of a Entra secured Blazor server web app hosted in an IFrame in another Entra secured Blazor server web app) using a SSO authentication based on Azure AD (Entra) APIs calls and/or on a specific MSAL.JS ssoSilent adoption, tailored to this specific context. All without (hopefully) the need to use a temporary window open/close.
The investigation continues... share your impressions and experiences on the matter... ;-)
PS: Personally I believe that if it will be possible to manage - in a "clean" way ... - the SSO problem (as described in the terms I summarized above), then the possibility of integrating (even simply via IFRAME) multiple Blazor Server "microfrontends" in an ASP.NET Core Blazor Server shell will be correctly and completely addressed.
Thanks for contacting us.
We're moving this issue to the .NET 9 Planning
milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. 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 issues.
To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.
For what it's worth, I've updated my Blazor.WebAssembly.SingleSpa NuGet package to provide experimental support for Blazor WebAssembly micro-frontends targeting .NET 8 and only .NET 8. Previously I had some .NET 6 and .NET 7 support, but .NET 8 brought a number of significant changes.
By and large the changes in .NET 8 were a net positive -- they simplified a lot of the things my package has to do in order to ensure a Blazor WASM app can be mounted/unmounted via single-spa. It did move some functionality to the .NET browser runtime, such as the code the selects the name of the in-browser .NET resource cache or the code that actually dynamic imports the hot reload script. That led to some more ... creative ... solutions, but nothing that really breaks the intent or semantics of the original framework code.
Since cleanly and safely mounting/unmounting a Blazor WASM app from the DOM and cleaning up the global state left by Blazor on the window object requires a bit of a fine dance, I also put together an experimental single-spa framework helper for Blazor WASM micro-frontends called blazor-wasm-single-spa. The whole point of this package is to make it easy to define the bootstrap, mount, and unmount lifecycle hooks that single-spa expects from each micro-frontend.
Getting these right is especially important if another independent Blazor WASM micro-frontend might subsequently load itself (along with its own version of the .NET runtime) and mount itself to the DOM. If you don't properly dispose and cleanup the first micro-frontend, it's easy to get into a situation where one micro-frontend is calling into the runtime of a different micro-frontend, with all the bad stuff that comes from that.
The following is a bare minimum example of what it takes to define the single-spa integration using this framework helper. This is pulled from my Blazing Lit project which is deployed live here:
import singleSpaBlazor from 'blazor-wasm-single-spa';
// Build the asset base URL from this JavaScript module's URL. The asset base URL must have a
// trailing slash for Blazor to apply it correctly.
const iLastSlash = import.meta.url.lastIndexOf('/');
const assetBaseUrl = import.meta.url.substring(0, iLastSlash + 1);
export const { bootstrap, mount, unmount } = singleSpaBlazor({
appTagName: 'mfe-catalog-app',
stylePaths: ['CatalogApp.styles.css'],
assetBaseUrl,
});
I've tried to keep things relatively simple with a lot of the magic tucked away inside the framework helper. To also demonstrate the ability to load multiple, independent Blazor micro-frontends and be able to cleanly switch between them, I added a second Blazor WASM micro-frontend to my Blazing Lit demo. This second one also incorporates MudBlazor and uses another (experimental) extension package to blazor-wasm-single-spa. This basically ensures the global state MudBlazor puts on the window object is cleared/restored when the micro-frontend is unmounted from and then later re-mounted to the DOM.
It's worth noting that while all of this is possible with Blazor WebAssembly, developing an application using a micro-frontend architecture definitely requires some thought also with regards to deployment and tooling. When developing, running, and testing a micro-frontend locally, you ideally shouldn't be forced to also locally run things like the app shell or any independent backend services needed just to have a fully running app. Otherwise, developer experience will be just super painful.
I'm using the above integration packages for some projects at work, and one of the things I developed to make locally running and testing micro-frontends easy is a local dev proxy based on YARP. When I run it, by default it simply proxies all requests to a dev instance of our app running remotely. However, if I specify a frontend override, then requests for that particular micro-frontend get routed to the localhost endpoint I specified. In practice, this means I run my dev proxy in one terminal in the background and then dotnet run
or dotnet watch
my micro-frontend. This gives me the benefit of being able to load and run my micro-frontend updates within the context of the entire application without having to deploy my updates first remotely.
The proxy also does a lot of magic to ensure things like WebAssembly debugging and hot reload work properly. There are lot of things that need to line up just right for these things to work in a micro-frontend context, some of which get complicated because the hot reload and debug endpoints are served up through "special" routes that get installed on your dev server via things like startup filters and startup hooks, a lot of which are described here. More often than not, the framework will reference these endpoints by absolute URL paths, which makes actually routing the requests to the micro-frontend's dev server tricky.
In the end I was able to get hot reload and client-side debugging working in a Blazor WASM micro-frontend, so I know it can be done. It would nicer though if things like the .NET SDK tooling (like dotnet-watch and the browser refresh scripts), the .NET browser runtime, and Blazor itself supported this micro-frontend scenario in a more first-class manner to minimize the software acrobatics that otherwise need to be done here.
However, until such a time when/if that occurs, if I'm able to find time to put together a minimal dev proxy that shows to support things like locally running a Blazor WASM micro-frontend as well as enabling debugging and hot reload, I'll be sure to post the link here. I think without something to fill in those pieces, it would be truly hard to effectively develop and maintain any micro-frontend solution.
It's interesting for us to consider running Blazor Server and WebAssembly applications in the same document as well as being able to mix them on the same apps. We've heard feedback that some people don't want to have some "proprietary business logic" on the client, and this offers a way to to do so. In the same vein, we can expect larger apps developed by several teams to run into conflicts on the versions used by their apps. Being able to run multiple blazor versions on the document, avoids this problem at the cost of increased app size.
UPDATE (from @MackinnonBuck)
Source: https://github.com/dotnet/aspnetcore/issues/38128#issuecomment-1559891706