-
Notifications
You must be signed in to change notification settings - Fork 10.2k
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
Unsure how to inject HttpClient for InteractiveAuto render mode (RC2) #51468
Comments
I think this may be what I was looking for... will try out and update the issue: Register common services |
You can use an abstraction over httpclient o even a mediator that can use its dependencies depending on the running mode. |
To utilize AutoMode, you could inject an interface and implement it using the HTTPClient for WASM and EntityFramework (or your preferred data retrieval method or an external API) for the server. Example:
For server-side registration:
For WASM:
In the razor page:
|
The biggest problem I have is that to obtain the baseAddress needed for the callback I need IWebAssemblyHostEnvironment, which is not available server-side. The client side is not being instantiated "in time" because in Auto mode, Blazor is streaming down WASM while trying to render the page without. So the control fails because it has no way to call back into the host. Register Common Services isn't working for me because I can't pass the WebAssemblyHostBuilder down into a method that would be callable by both server side and client side to register the common service (an HttpClient service). I feel like most of the documentation (understandably) is still derived from the world where you have chosen Blazor WebAssembly or Blazor Server and here at this intersection of InteractiveAuto, there are gaps in the docs that I can't figure out how to fill in at the moment. |
To be more clear, this is the service registration I am looking to register in a common way that will work for server-side and client-side and therefore be available for use with InteractiveAuto render mode:
|
Added sample on GitHub: https://github.com/SpaceShot/InteractiveAutoSample |
I'm having the same problem. Exemple: To Wasm use: To Server use: |
one way to do it: SpaceShot/InteractiveAutoSample#1 |
Why is the API called twice when reloading the page? |
because of prerendering that happens first on the server then wasm is loaded and it's called again. |
if you want to change this you either inject a service to detect prerendering or use the OnAfterRenderAsync hook instead of the OnInitlializedAsync Hook. Or disable prerendering.
|
PS: The Template you are using is the "Global WASM" Template so everything is running in WASM and you can't use InteractiveServer. What you see is the prerendering that takes place on the Server. |
Thanks for the pull request @mahald. I'll check it out but I think I see what you are saying. You've created an implementation where if you're on Server Interactivity then it just calls the external service and if you're on WASM then it calls back into the local endpoint. I did use the Global InteractiveAuto template. Mostly I am playing with InteractiveAuto mode to try and understand the nuances like this. |
Why not use the IHttpClientFactory with a named HttpClient to your third party api? |
Hi @bcheung4589 I have been experimenting with InteractiveAuto, although really it can all be applied to InteractiveWebAssembly as well. In this mode, I presented a sample app with a sample call to a third party API. Let's imagine that the API to be called isn't open to the public and I need to protect the keys or credentials or access to that API. I choose not to simply call it from the component, which resides in the Client project of the Blazor solution, because then it would be available to prying eyes of anyone who used the application and had the client DLLs streamed down to their browser. Therefore, my goal was to implement a sort of backend-for-frontend pattern, where the call was safely protected on the server. In this case, I was calling back into a minimal API in the web server itself. I was then looking for how to make that callback from client and server for both scenarios in InteractiveAuto. Thanks to @mahald, I learned I was thinking about the solution too naively, and that when I am on the server, why not simply make the call. His solution reminded me implementing the call as a service makes that easier and then I get what I want. When using a component with server interactivity, the call is made safely. When using a component with webassembly interactivity, the call is essentially marshaled to the server to make for the component, keeping whatever secrets safe. In this "new world" of InteractiveAuto, I had been exploring how to perform functions like this, since that world essentially says make your component "client-side ready" and then you're good for whatever happens. My sample is designed for a small web app where maybe it would be fine to keep it all together as shown. In a larger enterprise app you might still use secure cookies with strict policy so that client components make calls safely to the server and then "forwarded" along to the target. The Duende BFF sample does this for you, but not every app needs enterprise level infrastructure. It depends. I think the outcome of this issue might be some documentation with InteractiveAuto specific advice, but I don't speak for the team. I'm grateful to @mahald for the sample as it really showed me the way and I'm using the technique now in the real app. |
In that case I really do have to recommend using the IHttpClientFactory.
This should work for both modes and isnt Server specific. Keep the appsettings.json ofcourse on server and never client, just a disclaimer.
The con is now, that your component is coupled to your server call and not the third-party call. If you need manipulation before sending the request to the third-party, then yes, you need the server as intermediate.
I have removed Duende as dependency in my project, and only use .NET Identity Core (with EF Core ofcourse).
There has not been a offered guide/advise by .NET team probably because its so new and they might want to see what we as community do. (just my thought :)) What is important to understand is the conceptuel design of the web app. 1 Component runs once in browser if mode is WebAssembly. If you would like to visualize this; try this (name as you see fit yourself, this is my personal preference): Shared project:
Client project:
/Program.cs
Server project:
/Program.cs
So now you can inject the IRenderContext into your components.
Now you have control in your components what to render in what mode. FACADE pattern// this is my own personal implementation, do whatever you like here Shared project:
/Facades/IClientContactFacade.cs
Client project:
/Program.cs
Server project:
/Program.cs
/Endpoints/ClientContacts.cs // Minimal API
As you probably noticed, the ClientContactFacade is just a layered HttpClient call between the component and server that just calls the endpoint which in turn calls the ServerFacade. Why? to make sure both client- and server calls, run the same code path.
|
I was hit by this today whilst using InterativeWebAssembly render mode globally after upgrading from a .net7 project.
Since I'm using the InterativeWebAssembly globally, I was confused to see this behaviour because I was expecting that mode to act just like the old wasm mode in net7. I remember watching a recent video suggesting that you have 2 versions of every component which fetches data, a client version (using http) and a server version (using direct service calls). However, why doesn't InterativeWebAssembly work like wasm did in .net7, with the little timer visible during page load? I wasn't expecting fancy magic unless I picked one of the other render modes such as auto... Update: I just discovered |
Without seeing your app, try turning off prerender and see what happens. Not saying I'm sure, when I get home I'll try to replicate your scenario as best I can.
…________________________________
From: Byte Projects ***@***.***>
Sent: Thursday, November 16, 2023 6:31:24 PM
To: dotnet/aspnetcore ***@***.***>
Cc: Chris Gomez ***@***.***>; Author ***@***.***>
Subject: Re: [dotnet/aspnetcore] Unsure how to inject HttpClient for InteractiveAuto render mode (RC2) (Issue #51468)
I was hit by this today whilst using InterativeWebAssembly render mode globally after upgrading from a .net7 project.
1. Home page (framework downloads) > Pricing page (with http call) > OK.
2. Hit F5 - page reloads instantly but the base url is null on the named HttpClient, presumably because the wasm hasn't downloaded yet.
Since I'm using the InterativeWebAssembly globally, I was confused to see this behaviour because I was expecting that mode to act just like the old wasm mode in net7.
I remember watching a recent video suggesting that you have 2 versions of every component which fetches data, a client version (using http) and a server version (using direct service calls).
However, why doesn't InterativeWebAssembly work like wasm did in .net7, with the little timer visible during page load? I wasn't expecting fancy magic unless I picked one of the other render modes such as auto...
—
Reply to this email directly, view it on GitHub<#51468 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AAEVHCHSXQJKFH6NR7AH3ALYE2O4ZAVCNFSM6AAAAAA6F7SS5SVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQMJVGQ4TIMBUGA>.
You are receiving this because you authored the thread.Message ID: ***@***.***>
|
Server and client are using the same lifecycle events. Therefore OnInitialized and OnInitializedAsync are called both on server prerendering and client when using interactive web assembly mode. I think we need a better self-explanatory programming model here. Is adding a parameter like |
hello, your facade thing helped me a lot bcheung4589 Thx. But the issue is that at the first render it calls OnInitializedAsync, it retieves my collection and shows it for 1or 2 sec and it disapears... very strange. If I click on a button to re-fill it, it works form client side with httpclient but I cannot understand why the first call in OnInitalizedAsync is clearer (server-side) (seems to be related to the component being loaded client side) |
Ive noticed this as well, my solution for now is not rendering on first call (so on prerendering I show "Module loading") which prevents the page flicker => which I find absolutely unacceptable. So Im doing it this way for now, which kinda defeats it purpose, as Im using the WASM part mostly. But currently dont have time to deep dive for a real solution :( On pages that I dont use Interactivity, I dont have that page-flicker issue. Another issue I have is that on the server call (so after prerendering) it doesnt hit middleware, unless you always use WASM (so it goes through an endpoint => calls middleware). But just running it InteractiveServer wont call middleware on its Server run, and registered scoped services in the ServiceCollection wont help. So if you are planning to get a certain Id from the Claims (in middleware) using Interactivity, it can become very tricky. Your results disappears because the second run is not doing something that it should, in my case it was: middleware isnt run, so my scoped TenantAccessor didnt get updated through middleware. |
@bcheung4589 ... ok you are facing exactly the same issues as me. So I m not crazy... Like you, I find that unacceptable too. Because what's the purpose of auto mode if you have this flickering with prerender on or be forced to wait the wasm loading before doing someting without prerender ? And like you, if i use some -autorizedview- between the call it's weird and I think it's due to my user middleware too. The final goal was to protect all the external api call with the server layer for all cases
With all the marketing they made... I think we will be able to manage some amazing stuff but I m blocked on my inital tentative for weird simple things.... |
You can solve this with the proposed solution in: #52350 |
Thx @bcheung4589 , For now, I m able to persist authentification/autorization state both side with this guy example I m able to develop component that can work both side because of your facade pattern.
Authentification is in place with Keycloack (openid), the tokens to access external api are stored in distributed cache (server side). Now what is missing, is the flickering with prerender... :-) I hope I will be able to find a solution... Why the rerender after the inital run when you have a perfect first version from the server... sad. |
Complicating a very simple thing with the latest release of .NET is not good.. definitely not good. |
Sorry I wanna ask one question, I am using Blazorapp Global for development, and I also encountered that the server side requires httpClient to support API usage. So I had to inject httpClient on the server side and encountered the problem of requiring BaseAddress. I looked at the example you provided, and I don’t understand that your Service on the client side also calls httpClient, but you are using the minimum API. And I use a traditional Controller. Is this the main difference? |
@danroth27 * Hi, few questions please:
|
@JasminSAB Hi, thank you very much for your question and explanation. I would like to ask, so is it still not recommended to use .Net 8.0 for building commercial web applications? Additionally, my English is not particularly good. I was wondering if you could provide some examples of your projects or some source codes for reference. I might understand better the development methods and architecture you are using. |
Hi Daniel, may you please involve? Many topics are mentioned here, there is a lot of improvements for next iterations. KR, |
Hi @JasminSAB. I'll try to answer your questions below:
With the Auto and WebAssembly render modes only the code in the referenced client project will be downloaded to the browser. Any components that you want to run client-side need to be in the dependency graph of the client project.
Could you please clarify what exactly doesn't work? If you insulate a component from server and client specific concerns then you should be able to use that component with any render mode regardless of whether the render mode was configured globally on the router or per component.
Agreed, documenting these cases is what this issue is meant to track, and we need to move faster on getting it addressed. I'll follow up on that.
You should be able to replicate the experience of an ASP.NET Core hosted Blazor WebAssembly app by enabling global WebAssembly rendering and disabling prerendering: https://learn.microsoft.com/aspnet/core/migration/70-80#convert-a-hosted-blazor-webassembly-app-into-a-blazor-web-app |
@Edemilorhea Instead of injecting an HttpClient into your component you can instead inject a service based on some interface you define. You can then provide two implementations for that service: one for the client that uses an HttpClient to call an API, and one for the server that accesses the required server-based resource directly. Whether you implement your API endpoint using a minimal APi or a controller should not matter. |
Hi @danroth27 Thank you for your answers, I have found the template that fits my needs, It's:
This is the git repository: https://github.com/JasminSAB/WebInteractivity-per-component My current problem is with the Test.razor component I have an HttpRequest that is sent to the backend service to the API/test controller, and this controller is e.g. covered with "Admin!" role, and here I have two scenarios:
I have tried two approaches to execute API end point - directly via HttpClient and also with IHttpClientFactory
You can have a look at both program.cs files I have left TODO comments with the changes that I made to the boilerplate code. My changes on the backend are:
My changes on frontend are:
|
[Sorry that I wrote a whole BOOK 📖 here! 🙈 ... but I want to call out a few subjects on this issue, bring everyone up to speed on the doc updates thus far, and mention what else I'm working for docs in this area. Please enjoy my novel! ... Let's call it Blazor War and Peaceful (web) APIs 😆] @SpaceShot ... I'm reading down this issue now for further work, particularly looking to address security aspects on #31973. I just merged a large update to address Dan's summary of critical points at #51468 (comment), including new sample apps over in the Blazor samples repo. The coverage is live but not finalized. The MVP Summit is blocking review at the moment. I wanted to get better coverage in place quickly, so I went ahead and merged it. There will be further updates after I receive feedback from @danroth27 and possibly @MackinnonBuck (and/or other PU engineers). WRT the remark that you made on WRT WRT In the former case ... and as far as I know nothing has changed from Blazor Server days ... "external" web API calls are still made using For BWAs and "internal" (web) APIs, the (web) API is in the server project of the BWA. As fully described here by @danroth27 and others, we'll pitch the service abstraction approach. With a server-side service implementation, the API is a garden variety API accessed directly by the server-side service. It's not a "web" API in the server context. It's just a normal API not accessed over a network, and that's why I've been putting "web" in parentheses. Under CSR, the client-side implementation of the service uses a preconfigured Both of these scenarios are now covered ... LIVE but in draft form ... in the article. We might not stick with "internal" and "external" (web) API language. We'll sort that out soon enough. I need feedback from @danroth27, @MackinnonBuck, and community 🐱🐱🐱. The layout of the content (i.e., sections and section heading titles) may also change. We'll see. I encourage those wanting an experience with both scenarios (and including Blazor WebAssembly apps that call a web API in a backend app) to check out the new guidance and run the sample apps. You can get a sense of the difference between these and the stability of the data during navigation across components with different render nodes because I included Interactive Server, Interactive WebAssembly, and Interactive Auto test components in the BWA sample app for both locations of APIs. The components also leverage the Persistent Component State API to avoid flickering when the components load (there's no streaming rendering, so it works well). The BWA app has two distinct APIs to demonstrate without confusion the "internal" (web) API (a movie list) and "external" web API (a todo list).
WRT typed I'm focusing on security aspects to resolve #31973, but it's likely that I'll confer with PU peeps before merging any updates. |
@guardrex I think it's important to note that a lot of people who call external apis would like to use the Balzor server side as some sort of proxy because it's very convenient to do it like this.
I use a facade/interface to do it like explained by another guy in this post and it's very elegant in automode (@bcheung4589 thx to him). Serverside implementation of the facade : you call the external api Clientside implementation of the facade : you call your blazor server endpoint that acts as a very simple reverse proxy => call you server side facade implementation and only forward the response back to the client. Example of Balzor serverside controller Forward method that you can call for all your endpoints using httpcontext
It's just perfect to be able to manage external api calls (auth / token / caching) server side but have the possibility to put components on auto mode. It's some sort of magic trick I really like. If you want an example. I have a public github that uses that for a simple project in Net 8. EDIT : LINKS Server side implementation: Client side implementation: Blazor ServerSide Controller: |
@fdonnet ... That's the pattern that was adopted for the BWA with OIDC BFF sample app ...
I'm glad you said something for two reasons ...
Yikes! 😄 ... That's a lot to chew on for organizing coverage! This is why I felt it best to deal with the security aspects separately from @danroth27's initial coverage request that mainly focuses on the simplest case of the API in the server project without authn/z. I'll work on it this week. |
@guardrex yes
that was exactly why I implemented this stuff like this. Because of my OIDC (Keycloak) auth. And I m happy that it works not to bad for a draft/test mini project. So, it seems perfect if you cover that in OIDC section because I think people will have questions about how to manage automode Balzor components when you call Api with auth in a cool and secure way ;) |
I agree. Just for completeness, the https://github.com/dotnet/blazor-samples/tree/main/8.0/BlazorWebAppOidc The difference is that the server doesn't need to make an external call, as there is no backend server web API. The app handles generating the weather data for SSR directly ... The client calls an endpoint (securely) ... The web API endpoint for that calls the server's method directly ... Can I toss in a double "Yikes! Yikes!" here? 😆 These patterns will need to be explained out further. Currently, the BWA+OIDC article focuses heavily on the OIDC aspects and doesn't get into the (web) API patterns. That was fine to get our coverage started, but it's time to extend the coverage into these aspects. I'll be working on it over the next few days. Hopefully, we'll have good coverage by early next week. |
I have explored various methods to solve the issue of how to send requests automatically, but most of them were incomplete or required a lot of work. Recently, I discovered that the Bit platform has an interesting solution to this problem. On this platform, there is a shared layer that contains an interface, let's say a "IDashboardController," which returns the required data to the server. On the client side, you use this interface, and on the server side, you implement its methods on the desired controller. Lets Call it Now DashboardController ,So far, this is a common approach and nothing new. However, what is interesting is that, as I discussed with the platform's development team, the actual controller is never injected into the client side! The implementation is merely a way to ensure that the methods are existed on the controller, and no server-side action is intended. The second fascinating part is that, due to the code generator they have created, when you inject and use this interface, it automatically starts building the interface's code and uses an HttpClient to send requests to the server on the user's behalf. This means that the requests are always made from the client-side, and there is no need to write additional code for the client, which is very appealing to me. For instance, if you want the interface to send requests automatically, you can implement a separate service class on the server side and add it to the dependency injection (DI) container. It would be fantastic if .NET itself provided such capabilities on the client side. |
The only novel aspect of this solution is the automatic generation of code for HttpClient; the rest is the same as what @danroth27 proposed in his example. |
Yes, the core concept is the same, but one is a starting point and the other is a final solution. One requires a lot of additional components to function, while the other is ready to use out-of-the-box. I didn't mean to imply that this solution is bad, but my concern is for newcomers who might find it challenging to navigate the complexities of creating a simple app. We need a straightforward solution for complex tasks, and while the community can provide alternatives, in the .NET ecosystem, we often prefer core tools developed by Microsoft. This way, we can be assured of ongoing maintenance and freely use them without relying heavily on third-party tools. |
Friendly reminder that docs need updating on this :) |
Are you sure that "sort of dummy" is the correct definition? Doesn't that imply that it's real, not a dummy one? |
@richardaubin ... The new 9.0 API is covered at ... One has to set the document version selector to "9.0" to see it. That link ☝️ will get you there, however, because it sets the "view" to the 9.0 version of the article via its query string. WRT They didn't approve "a sort of dummy HttpContext with partial information" language. What you see there is what was approved. We have a spot that requires a bit more explanation for the |
@guardrex - could we possibly have a more thorough explanation of the difference between the IHttpContextAccessor.HttpContext and the one that's available as a CascadingParameter? e.g. are they functionally equivalent, and what are the reasons to prefer one over the other? |
As far as I know, the existing text is what the product unit wants (or wanted at the time) to say about it. I'm 👂 for them to say if they want to add or change what's there. |
Actually I won't mind if it doesn't make it into the docs, if there's anyone here that can give an unofficial explanation 🙏 |
I'm not sure if this is a problem in RC2 or if I'm just too new to InteractiveAuto render mode.
I have a component in the client project which calls back to the server for its api calls since the actual call to the third party api needs to be on the server (auth tokens, secrets, and the like).
Per the documentation in Call a web API in Blazor on the WebAssembly pivot, it says I can register an HttpClient back to the base address with:
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
Then I wrote the component to make the call back to localhost... then the server project happens to have a minimal api route to make the "real" call.
This has worked fine as long as I don't put the component on the home page. What I observe happening is that if I place the component on any "secondary page" and let the home page load, the wasm assembly must be streamed down and a breakpoint on the above line fires.
If I put it on the home page, that breakpoint doesn't fire, but the component is rendered. In OnInitializeAsync() it tries to use the HttpClient to call home but BaseAddress wasn't set, so it fails.
Should components intended to be used with InteractiveAuto look to a different part of the lifecycle?
I think another option was to also register an HttpClient in the server Program.cs, but I can't figure out how to do this so that it is picked up in the initial render (perhaps it is using Blazor Server for that or prerendering... it's auto after all).
I'm not sure this is a bug or I'm ahead of the curve on the documentation for Auto modes. I can provide a sample if this behavior seems like a potential bug.
Note: All components and pages are in the Client project. I am experimenting with the experience for global InteractiveAuto. I only moved Error.razor to server side per known RC2 issue.
The text was updated successfully, but these errors were encountered: