Skip to content

Blazor call web API topic with sample #12477

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

Merged
merged 8 commits into from
Jun 26, 2019
Merged

Conversation

guardrex
Copy link
Collaborator

@guardrex guardrex commented May 20, 2019

Fixes #10743

Internal Review Topic

  • Not crazy about the title, but we can work on that.
  • The sample app component (Pages/CallWebAPI.razor) is built in concert with our web API tutorial. The examples in the topic and the sample app code are set up for a happy path by merely running the web API tutorial app and then running the code in the topic or the Razor component in sample against that service.
  • I'm mostly concerned about the HttpClient and HttpRequestMessage with Fetch API request options section. It's the most challenging bit to organize and explain ... lot's 'o concepts there ... and there are probably more concepts to add.
  • This 1st draft is client-side-y in nature. There is no registered service of type 'System.Net.Http.HttpClient'. ... I see what you mean on the engineering issue. Will take ur advice ... IHttpClientFactory for server-side.

@guardrex
Copy link
Collaborator Author

guardrex commented May 20, 2019

If you can guide me on the untrusted cert situation ... how to handle it better than this (just have them specify whatever cert that they plan to use here?) ... along with any gotchas! ... then I could roll this into the topic (but not the sample app given that we only have a client-side common sample for our Blazor topics at this time).

[EDIT] Also noticed that the Http requests topic does it a bit differently than what I show from a dev's post. https://docs.microsoft.com/aspnet/core/fundamentals/http-requests

Alternatively: We could label this topic clearly (a NOTE) that it's for Blazor client-side at this time and cross-link the engineering issue wrt the server-side design.

btw ... just curious @dazinator ... is this about what you ended up doing 👇?

Project file

<ItemGroup>
  <PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup>

Startup.ConfigureServices

services.AddScoped(s =>
{
    var uriHelper = s.GetRequiredService<IUriHelper>();
    return new HttpClient
    {
        BaseAddress = new Uri(uriHelper.GetBaseUri()),
    };
});

OR w/o a UriHelper setting the base address and using the AddHttpClient method, this also works (but might be unstable given a dev's post on the subject) ...

services.AddHttpClient();

Component

@using System.Net.Http

@code {
    private async Task MakeHttpClientRequest()
    {
        using (var httpClientHandler = new HttpClientHandler())
        {
            httpClientHandler.ServerCertificateCustomValidationCallback = 
                (message, cert, chain, errors) => true;
    
            using (var client = new HttpClient(httpClientHandler))
            {
                // Use client
            }
        }
    }
}

@guardrex
Copy link
Collaborator Author

guardrex commented Jun 6, 2019

@danroth27 Shall we proceed with this? The client-side here looks good ... works great ... based on the web API sample provided by our existing ASP.NET Core web API sample app topic.

The problem is server-side, which I'm aware is in-design. We could either:

  • Offer a workaround ☝️ (last comment) with some engineering help to make it good (e.g., get a little into the weeds on how to validate a cert there with that code).
  • Go ahead with the client-side-only topic here. Add a NOTE at the top about server-side being in-design, and cross-link the note to Making HTTP requests from Blazor apps (aspnet/AspNetCore #10397). Open an issue for follow-up on the server-side later.

Copy link
Member

@danroth27 danroth27 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good! Just a few suggestions.

@dazinator
Copy link

dazinator commented Jun 21, 2019

@guardrex

btw ... just curious @dazinator ... is this about what you ended up doing

Yes I registered my singleton httpclient with DI, and configured it myself in the factory delegate.

However.. my eyes have just been opened to the IHttpClientFactory way: dotnet/aspnetcore#11440 (comment)

So today I am now doing this:

  services.AddTransient<WebAssemblyHttpMessageHandler>(); // also register other custom handlers you see below..

 services.AddHttpClient<IMyService, MyService>()
                    .ConfigurePrimaryHttpMessageHandler<WebAssemblyHttpMessageHandler>()
                    .AddHttpMessageHandler<JwtAccessTokenHeaderHandler>()
                    .AddHttpMessageHandler<CsrfHeaderHandler>();    

I don't directly inject HttpClient into my components any longer, I inject a typed service as above - like IMyService which takes a configured HttpClient instance in it's constructor. I guess i should also remove the default HttpClient registration that blazor adds to make sure an unconfigured instance cannot be accidetnally injected into a component by someone who doesn't know any better.

Are there known issues with doing it this way within a blazor client app?

@guardrex
Copy link
Collaborator Author

guardrex commented Jun 21, 2019

@danroth27 Update pass made.

  • I'm still a little off perhaps on server-side. I was under the impression from the in-design issue that it wouldn't be good at this time to add this remark without a little more clarification as to where the approach works ...

    In Blazor server-side apps, the <xref:System.Net.Http.HttpClient> implementation is provided by .NET Core.

    ... as that makes it seem like the HttpClient service is available for server-side ... however ... see if the NOTE and text that I updated works for the server-side scenario. I might not be too far off what we need to do.

    Also, we have two approaches as workaround now. We have what I show at Blazor call web API topic with sample #12477 (comment), and we have what @dazinator is doing at Blazor call web API topic with sample #12477 (comment). Either or both could be written in as a workaround, but Making HTTP requests from Blazor apps aspnetcore#10397 is marked for Pre8 ... so that's not a very long wait if we just skip it. If a reader were to open an issue here, I'd just point them to this PR to read those comments. IIRC, I noted this PR on your in-design engineering issue, too.

  • It looks like @using System.Net.Http isn't required if there's an @page directive. Does that make sense? ... I addressed it on the last commit. We might need to work that text a little more.

  • Finally, there's the ? that you pinged Steve on. I have no i-dear wrt the other Fetch API things that can be set. I'm all 👂 👂 👂 for what we should say there.

@danroth27
Copy link
Member

see if the NOTE and text that I updated works for the server-side scenario.

I'm not a fan of the note 😜. This topic isn't just for client-side Blazor. Also, the statement about how the HttpClient is implemented (using fetch or .NET Core's implementation) is separate from whether it is available as an injectable service. I think all we need to say is 1. HttpClient in client-side Blazor is based on fetch, while in server-side Blazor it's the normal .NET Core implementation, and 2. `HttpClient is available as a preconfigured service in client-side Blazor but not server-side Blazor. You should ignore the design issue for now.

The thing to realize here is that you don't typically want to make HTTP requests from a server-side Blazor app back to the same server. Instead you should abstract away this concern using your own service that has different implementations depending on where the app is host. For example, you would define an IWeatherForecast service and configure an implementation on the client that uses HttpClient to call a Web API and on the server just access the backing data store (probably a DB) directly. See https://github.com/danroth27/BlazorDualMode for an example of this setup.

It looks like @using System.Net.Http isn't required if there's an @page directive. Does that make sense?

Nope 😄. Blazor doesn't include a using statement for System.Net.Http by default. You have to add it. In the templates this is done in the root _Imports.razor file.

Finally, there's the ? that you pinged Steve on. I have no i-dear wrt the other Fetch API things that can be set.

Let's just ignore this for now. We can add more details later if we're missing something.

Copy link
Member

@danroth27 danroth27 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few minor tweaks, but otherwise I think this looks fine.

@guardrex
Copy link
Collaborator Author

guardrex commented Jun 23, 2019

You see where this all went wrong, right? 😄 ...

`HttpClient is available as a preconfigured service in client-side Blazor but not server-side Blazor.

That was the hang-up. What I was doing is making it all about injected HttpClient.

I see what you want now, but it seems like if we're going to wait on the design for server-side (not mention it) and not show a workaround (e.g., HttpClientHandler), we should have an example of using the .NET Core implementation (just a class used by the component, right?) in the server-side scenario.

In the templates this is done in the root _Imports.razor file.

😄 lol ..... I didn't LOOK! 🙈 lol

@danroth27
Copy link
Member

I see what you want now, but it seems like if we're going to wait on the design for server-side (not mention it) and not show a workaround (e.g., HttpClientHandler), we should have an example of using the .NET Core implementation (just a class used by the component, right?) in the server-side scenario.

Sorry about my confusion, but what workaround are you referring to? How is HttpClientHandler relevant? On the server the story for making HTTP requests is no different than it is for any other ASP.NET Core app. If you want a DI based solution on the server you can use IHttpClientFactory and friends. So what class would you show?

@guardrex
Copy link
Collaborator Author

In order to use HttpClient, you said that devs were 'jumping thru hoops.' I didn't know what devs were doing, and someone mentioned HttpClientHandler like I showed at #12477 (comment). I gave it a shot, and it does work (you know ... it gets u some var client = new HttpClient(httpClientHandler) action in the RC).

just a class used by the component

Nevermind that ... I thought for sec that we wouldn't use the IHttpClientFactory directly in an RC but put it into a separate class. I see now that yes ... that's exactly what you want ... inject it into the RC. Gotcha.

So, the topic will go with IHttpClientFactory, and we can cross-link that content for almost all of the coverage. Perhaps, a quick example might be nice. Let me see how that composes. I hope that it would only take a short code block. If it looks unnecessary, then we can cut it.

@danroth27
Copy link
Member

In order to use HttpClient, you said that devs were 'jumping thru hoops.'

I think what this may be referring to is that some folks have been adding an HttpClient as a service in their server-side Blazor apps to create some symmetry with client-side Blazor. But most of the time I don't think that's what you really want, because then in your server-side Blazor app you end up making HTTP requests back to your own server. Instead, if you want to use the same component code independent of where the app is hosted, you should create a service that abstracts away how the data is requested.

In Blazor server-side apps your making HTTP requests to third party service and the story is no different than it is for existing ASP.NET Core apps, so we should just say that and point to https://docs.microsoft.com/aspnet/core/fundamentals/http-requests. The HttpClient service setup in Blazor client-side apps is specifically setup for making requests back to the server of origin.

@guardrex
Copy link
Collaborator Author

guardrex commented Jun 24, 2019

@danroth27 Recommend one more quick 👁️ on this to confirm that the feedback updates are correct.

I ended up not going with a server-side example. I didn't put it in because it's fairly trivial to implement. As you suggest, I punt the reader over to the HTTP Requests topic. However, let me know if you'd like to see an example here.

@guardrex guardrex requested a review from scottaddie June 24, 2019 13:27
@guardrex
Copy link
Collaborator Author

Thx @scottaddie 🚀

WRT the constants situation: It breaks 💥 in a non-page component (the HTTP Request Tester component) (and Steve has it that way in the engineering repo version of that component), but it's 👍 in a page component (the Call Web API component). Therefore, I made it a constant only in the page component. Idk if that should be reported in engineering, but Dan is 👂 here and will let us know.

Copy link
Member

@danroth27 danroth27 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more comments, but otherwise looks good!

@guardrex
Copy link
Collaborator Author

@scottaddie Do u want to run a final 👁️ over the updates?

@danroth27
Copy link
Member

:shipit:

@scottaddie scottaddie merged commit aa49057 into master Jun 26, 2019
@delete-merged-branch delete-merged-branch bot deleted the guardrex/blazor-to-webAPI branch June 26, 2019 14:40
@Ozkr16
Copy link

Ozkr16 commented Jan 19, 2020

In order to use HttpClient, you said that devs were 'jumping thru hoops.'

I think what this may be referring to is that some folks have been adding an HttpClient as a service in their server-side Blazor apps to create some symmetry with client-side Blazor. But most of the time I don't think that's what you really want, because then in your server-side Blazor app you end up making HTTP requests back to your own server. Instead, if you want to use the same component code independent of where the app is hosted, you should create a service that abstracts away how the data is requested.

In Blazor server-side apps your making HTTP requests to third party service and the story is no different than it is for existing ASP.NET Core apps, so we should just say that and point to https://docs.microsoft.com/aspnet/core/fundamentals/http-requests. The HttpClient service setup in Blazor client-side apps is specifically setup for making requests back to the server of origin.

Hey @danroth27, this is an extremely useful insight. When reading thought the documentation I was unable to find something like this. For people with an ASP.Net Core app development background (specially those of us who deal with microservices architectures), the Blazor Server HTTP Client situation can be confusing. Thanks for clearing this out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Call web APIs from Blazor topic
5 participants