--- sidebar_position: 3 title: Make HTTP requests --- ## Data format Two operations exist to retrieve data from an API, the first is to retrieve raw data in JSON format, the second is to use serialization / deserialization. In our case, the library used implements serialization / deserialization by default. In order to be able to manipulate our data we will use our `Item` component class locally, the data received comes from the serialization of the `Item` class of the server. ```csharp title="Models/Item.cs" public class Item { public int Id { get; set; } public string DisplayName { get; set; } public string Name { get; set; } public int StackSize { get; set; } public int MaxDurability { get; set; } public List EnchantCategories { get; set; } public List RepairWith { get; set; } public DateTime CreatedDate { get; set; } public DateTime? UpdatedDate { get; set; } public string ImageBase64 { get; set; } } ``` :::info Adding or deleting fields in our model does not generate an error when retrieving data. ::: ## Using the IOC Thanks to our IOC we will therefore call our API in a specific service implementing the `IDataService` interface. Create the `DataApiService` class: ```csharp title="Services/DataApiService.cs" public class DataApiService : IDataService { private readonly HttpClient _http; public DataApiService( HttpClient http) { _http = http; } public async Task Add(ItemModel model) { // Get the item var item = ItemFactory.Create(model); // Save the data await _http.PostAsJsonAsync("https://localhost:7234/api/Crafting/", item); } public async Task Count() { return await _http.GetFromJsonAsync("https://localhost:7234/api/Crafting/count"); } public async Task> List(int currentPage, int pageSize) { return await _http.GetFromJsonAsync>($"https://localhost:7234/api/Crafting/?currentPage={currentPage}&pageSize={pageSize}"); } public async Task GetById(int id) { return await _http.GetFromJsonAsync($"https://localhost:7234/api/Crafting/{id}"); } public async Task Update(int id, ItemModel model) { // Get the item var item = ItemFactory.Create(model); await _http.PutAsJsonAsync($"https://localhost:7234/api/Crafting/{id}", item); } public async Task Delete(int id) { await _http.DeleteAsync($"https://localhost:7234/api/Crafting/{id}"); } public async Task> GetRecipes() { return await _http.GetFromJsonAsync>("https://localhost:7234/api/Crafting/recipe"); } } ``` We now have a class allowing us to pass all of our calls through an API. ## Register the data service Open the `Program.cs` file and edit the following line: ```csharp title="Program.cs" ... builder.Services.AddScoped(); ... ``` ## Add the API sample to your project Download this [project](/Minecraft.Crafting.Api.zip). :::caution If you have a 404 error with the download, remove the last `/` at the end of the url. Example : `.zip/` => `.zip` ::: Unzip the file in the directory of your project, at the same place of the directory of the Blazor Project. Example: ![Sample Api Location](/img/api/sample-api-location.png) On Visual Studio, click right on the solution and choose `Add => Existing Project...` ![Add Existing Project](/img/api/add-existing-project.png) Select the file `Minecraft.Crafting.Api.csproj` in the directory `Minecraft.Crafting.Api`. Your solution now contains two projects, your client and the sample API. ## How to start two projects at same time in Visual Studio For test your client with the API, you have to start the two projects at same time. For this, on Visual Studio, click right on the solution and choose `Properties` ![Solution Properties](/img/api/solution-properties.png) On the new screen select "Multiple startup projects" and select "Start" for the two projects. ![Multiple startup projects](/img/api/multiple-startup-projects.png) When you start debug mode, the two projects are started. ## Concept: IHttpClientFactory An `IHttpClientFactory` can be registered and used to configure and create HttpClient instances in an app. `IHttpClientFactory` offers the following benefits: * Provides a central location for naming and configuring logical `HttpClient` instances. For example, a client named github could be registered and configured to access GitHub. A default client can be registered for general access. * Codifies the concept of outgoing middleware via delegating handlers in `HttpClient`. Provides extensions for Polly-based middleware to take advantage of delegating handlers in `HttpClient`. * Manages the pooling and lifetime of underlying `HttpClientMessageHandler` instances. Automatic management avoids common DNS (Domain Name System) problems that occur when manually managing `HttpClient` lifetimes. * Adds a configurable logging experience (via `ILogger`) for all requests sent through clients created by the factory. ### Consumption patterns There are several ways IHttpClientFactory can be used in an app: * Basic usage * Named clients * Typed clients * Generated clients The best approach depends upon the app's requirements. #### Basic usage Register `IHttpClientFactory` by calling `AddHttpClient` in `Program.cs`: ```csharp var builder = WebApplication.CreateBuilder(args); // Add services to the container. // highlight-next-line builder.Services.AddHttpClient(); ``` An `IHttpClientFactory` can be requested using dependency injection (DI). The following code uses `IHttpClientFactory` to create an `HttpClient` instance: ```csharp public class BasicModel : PageModel { private readonly IHttpClientFactory _httpClientFactory; // highlight-next-line public BasicModel(IHttpClientFactory httpClientFactory) => _httpClientFactory = httpClientFactory; public IEnumerable? GitHubBranches { get; set; } public async Task OnGet() { var httpRequestMessage = new HttpRequestMessage( HttpMethod.Get, "https://api.github.com/repos/dotnet/AspNetCore.Docs/branches") { Headers = { { HeaderNames.Accept, "application/vnd.github.v3+json" }, { HeaderNames.UserAgent, "HttpRequestsSample" } } }; // highlight-next-line var httpClient = _httpClientFactory.CreateClient(); var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage); if (httpResponseMessage.IsSuccessStatusCode) { using var contentStream = await httpResponseMessage.Content.ReadAsStreamAsync(); GitHubBranches = await JsonSerializer.DeserializeAsync >(contentStream); } } } ``` Using `IHttpClientFactory` like in the preceding example is a good way to refactor an existing app. It has no impact on how `HttpClient` is used. In places where `HttpClient` instances are created in an existing app, replace those occurrences with calls to `CreateClient`. #### Named clients Named clients are a good choice when: * The app requires many distinct uses of `HttpClient`. * Many `HttpClients` have different configuration. Specify configuration for a named `HttpClient` during its registration in `Program.cs`: ```csharp builder.Services.AddHttpClient("GitHub", httpClient => { httpClient.BaseAddress = new Uri("https://api.github.com/"); // using Microsoft.Net.Http.Headers; // The GitHub API requires two headers. httpClient.DefaultRequestHeaders.Add( HeaderNames.Accept, "application/vnd.github.v3+json"); httpClient.DefaultRequestHeaders.Add( HeaderNames.UserAgent, "HttpRequestsSample"); }); ``` In the preceding code the client is configured with: * The base address https://api.github.com/. * Two headers required to work with the GitHub API. ##### CreateClient Each time CreateClient is called: * A new instance of `HttpClient` is created. * The configuration action is called. To create a named client, pass its name into `CreateClient`: ```csharp public class NamedClientModel : PageModel { private readonly IHttpClientFactory _httpClientFactory; public NamedClientModel(IHttpClientFactory httpClientFactory) => _httpClientFactory = httpClientFactory; public IEnumerable? GitHubBranches { get; set; } public async Task OnGet() { // highlight-next-line var httpClient = _httpClientFactory.CreateClient("GitHub"); var httpResponseMessage = await httpClient.GetAsync( "repos/dotnet/AspNetCore.Docs/branches"); if (httpResponseMessage.IsSuccessStatusCode) { using var contentStream = await httpResponseMessage.Content.ReadAsStreamAsync(); GitHubBranches = await JsonSerializer.DeserializeAsync >(contentStream); } } } ``` In the preceding code, the request doesn't need to specify a hostname. The code can pass just the path, since the base address configured for the client is used. #### Typed clients Typed clients: * Provide the same capabilities as named clients without the need to use strings as keys. * Provides IntelliSense and compiler help when consuming clients. * Provide a single location to configure and interact with a particular `HttpClient`. For example, a single typed client might be used: * For a single backend endpoint. * To encapsulate all logic dealing with the endpoint. * Work with DI and can be injected where required in the app. A typed client accepts an `HttpClient` parameter in its constructor: ```csharp public class GitHubService { private readonly HttpClient _httpClient; // highlight-next-line public GitHubService(HttpClient httpClient) { _httpClient = httpClient; _httpClient.BaseAddress = new Uri("https://api.github.com/"); // using Microsoft.Net.Http.Headers; // The GitHub API requires two headers. _httpClient.DefaultRequestHeaders.Add( HeaderNames.Accept, "application/vnd.github.v3+json"); _httpClient.DefaultRequestHeaders.Add( HeaderNames.UserAgent, "HttpRequestsSample"); } public async Task?> GetAspNetCoreDocsBranchesAsync() => await _httpClient.GetFromJsonAsync>( "repos/dotnet/AspNetCore.Docs/branches"); } ``` In the preceding code: * The configuration is moved into the typed client. * The provided `HttpClient` instance is stored as a private field. API-specific methods can be created that expose `HttpClient` functionality. For example, the `GetAspNetCoreDocsBranches` method encapsulates code to retrieve docs GitHub branches. The following code calls `AddHttpClient` in `Program.cs` to register the `GitHubService` typed client class: ```csharp builder.Services.AddHttpClient(); ``` The typed client is registered as transient with DI. In the preceding code, `AddHttpClient` registers `GitHubService` as a transient service. This registration uses a factory method to: * Create an instance of `HttpClient`. * Create an instance of `GitHubService`, passing in the instance of `HttpClient` to its constructor. The typed client can be injected and consumed directly: ```csharp public class TypedClientModel : PageModel { private readonly GitHubService _gitHubService; // highlight-next-line public TypedClientModel(GitHubService gitHubService) => _gitHubService = gitHubService; public IEnumerable? GitHubBranches { get; set; } public async Task OnGet() { try { // highlight-next-line GitHubBranches = await _gitHubService.GetAspNetCoreDocsBranchesAsync(); } catch (HttpRequestException) { // ... } } } ``` The configuration for a typed client can also be specified during its registration in `Program.cs`, rather than in the typed client's constructor: ```csharp builder.Services.AddHttpClient(httpClient => { httpClient.BaseAddress = new Uri("https://api.github.com/"); // ... }); ``` #### Generated clients `IHttpClientFactory` can be used in combination with third-party libraries such as Refit. Refit is a REST library for .NET. It converts REST APIs into live interfaces. Call `AddRefitClient` to generate a dynamic implementation of an interface, which uses `HttpClient` to make the external HTTP calls. A custom interface represents the external API: ```csharp public interface IGitHubClient { [Get("/repos/dotnet/AspNetCore.Docs/branches")] Task> GetAspNetCoreDocsBranchesAsync(); } ``` Call `AddRefitClient` to generate the dynamic implementation and then call `ConfigureHttpClient` to configure the underlying `HttpClient`: ```csharp // highlight-next-line builder.Services.AddRefitClient() .ConfigureHttpClient(httpClient => { httpClient.BaseAddress = new Uri("https://api.github.com/"); // using Microsoft.Net.Http.Headers; // The GitHub API requires two headers. httpClient.DefaultRequestHeaders.Add( HeaderNames.Accept, "application/vnd.github.v3+json"); httpClient.DefaultRequestHeaders.Add( HeaderNames.UserAgent, "HttpRequestsSample"); }); ``` Use DI to access the dynamic implementation of `IGitHubClient`: ```csharp public class RefitModel : PageModel { private readonly IGitHubClient _gitHubClient; // highlight-next-line public RefitModel(IGitHubClient gitHubClient) => _gitHubClient = gitHubClient; public IEnumerable? GitHubBranches { get; set; } public async Task OnGet() { try { // highlight-next-line GitHubBranches = await _gitHubClient.GetAspNetCoreDocsBranchesAsync(); } catch (ApiException) { // ... } } } ``` ### HttpClient request type HttpClient supports other HTTP verbs: | Properties | Verbe | | ---- | ---- | | Delete | Represents an HTTP DELETE protocol method. | | Get | Represents an HTTP GET protocol method. | | Head | Represents an HTTP HEAD protocol method. The HEAD method is identical to GET except that the server only returns message-headers in the response, without a message-body. | | Method | An HTTP method. | | Options | Represents an HTTP OPTIONS protocol method. | | Patch | Gets the HTTP PATCH protocol method. | | Post | Represents an HTTP POST protocol method that is used to post a new entity as an addition to a URI. | | Put | Represents an HTTP PUT protocol method that is used to replace an entity identified by a URI. | | Trace | Represents an HTTP TRACE protocol method. |