-
Notifications
You must be signed in to change notification settings - Fork 10.3k
[Blazor] Simplify writing code that works across different environments #60312
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
This is a great initiative. Using pre-rendered WASM components cause a lot of trouble having to register the DI services on the server side. The solution is to disable pre-rendering so that the DI services are only resolved on the client, but it creates for a really bad user experience not being able to provide loading states or pre-rendered content. |
Honestly I kinda hate this. Using conditional compilation feels like something I have to do because Blazor has a flaw rather than being the right approach. I'd rather see Blazor handle this in a more automated way.
Make the "auto" rendering mode actually be auto. |
I like the idea but it won't work in practice.. Blazor Server components can directly reference the data model, or infra projects, or the dbcontext.. giving WASM access to those class libraries would be catastrophic.. WASM projects should only reference DTO project and call endpoints This issue is mainly for solving DI two times, i think |
I've done a couple of projects with dual injection classes, one for client side, one for server side. It's fine, but a bit laborious. Typically you build your serverside services that do the real thing, and then for the Wasm you write very thin clientside services that are nothing but a proxy via controllers or minimal API wrappers, to call back to the server instance of the services and do the actual work. The proxies are pure boilerplate and add almost nothing. Perhaps the right answer is to have some convention or attribute based automatic creation of all of that. So you'd write your serverside services, mark them up with an attribute like |
https://github.com/reactiveui/refit Refit interfaces already do most of that, I don't think thats Blazor responsibility |
What do you mean "refit interface"? I guess my point was that in @javiercn's OP, the implementation for wasm and server is #ifdef'd to separate them. What I'm saying is, go one step further and have them implicitly generated without needing any written code at all. |
As a more specific example of where this issue is problematic is with Auth related code. If you have an Auth service for the client, it likely depends on browser storage for cookies, tokens etc, and also the http client for headers, tokens etc. It's not really safe to implement impersonation of the client for the server side service, and a second Auth flow for the server side creates its own issues too. Nevermind handling that handover gracefully and whatever security vulnerabilities that whole mess introduced. So the correct approach seems to be to have a dummy implementation for the server side that does nothing and then the client side service does the actual authentication flow. Auth is a very common requirement and the current approach makes it easy to write potentially insecure code. The fact that even setting the render mode from "Interactive Auto" to "Interactive Webassembly" which the documentation says is entirely client side doesn't disable the requirement adds to the confusion. Which in turn leads to people blindly registering the services on both sides so the project will run. I understand that this is a skill or knowledge issue but making this behaviour and the requirements to correctly support it more explicit goes a long ways towards making the framework more approachable. So at the very least I'd suggest the documentation and templates should be updated. |
@garrettlondon1 Why wouldn't SourceGenerators be a good fit for this? As I and @Webreaper noted, if you have the interface and maybe some attributes you have everything you need to source-generate a minimal API endpoint that can use my server-side implementation to return the data, and a wasm-side implementation of the interface that will use the generated API endpoint to retrieve the data on the client ... no need for me to manually code that. |
I have written 2 source generators, 1 for the client and 1 for the server for exactly this use case. The WASM generates Proxy version with http client, the server one a controller implementation. On the server side we then have a handwritten Service that implements the actual logic of the API call. Works like a charm. For the services bit we ended up with a convention of having "VoidServices" for pre-rendering of client side services. Just empty service implementations. I'm not really a fan of multi-targeting, unless it is hard to make a mistake and leak server side assembly references to the client. Having different projects (and I use solution folders) makes it clear where an assembly is accessible from, if it's all in one assembly it is more difficult to see this. |
@rogihee that sounds good. It would also need to account for some sort of authentication on the api endpoints. Or at least the possibility of them requiring authentication. |
Currently, when a component/service needs to run on the server and on webassembly, developers are forced to create at least two versions of the service, put each version in a different location depending on whether it runs on the server or on webassembly, and register things in the DI container twice.
This in many cases is hard to get right, as it requires several steps and it's easy to miss something.
As an alternative, we can consider bringing back multi-targeting support. With this approach, it should be possible for the app to define both implementations on the same location and use the same call to register the services in one go. We can update the template to support such pattern when webassembly is involved.
One such example is:
The text was updated successfully, but these errors were encountered: