How-To: Configure HttpClient with Custom Endpoint Options
It's often necessary to include an API key alongside requests to a web API. This can be done by adding a header to the request. The steps below will show you how to easily specify custom options, such as an access token, when adding an endpoint. You can then configure the associated HttpClient from these options.
Pre-requisites
An environment set up for developing Uno Platform applications
Basic conceptual understanding of accessing web resources using HTTP requests
Knowledge of how to register an endpoint for HTTP requests
Step-by-step
Important
This guide assumes you used the template wizard or dotnet new unoapp to create your solution. If not, it is recommended that you follow the Creating an application with Uno.Extensions documentation to create an application from the template.
1. Preparing for custom endpoint options
Create a new class called
CustomEndpointOptionsin the shared project. This class extendsEndpointOptionsto allow you to specify custom options for the endpointpublic class CustomEndpointOptions : EndpointOptions { public string ApiKey { get; set; } }In this example, we intend to add an access token to the request header. The
ApiKeyproperty will be used to store the token.The
EndpointOptionsclass is a base class that provides aUrlproperty. This property is used to specify the URL of the endpoint.Subclassing
EndpointOptionswill allow you to configure theHttpClientassociated with the endpoint — all from a single configuration section.
2. Defining the endpoint
Add
Httpto the<UnoFeatures>property in the Class Library (.csproj) file.<UnoFeatures> Material; Extensions; + Http; Toolkit; MVUX; </UnoFeatures>Enable HTTP by calling the
UseHttp()method to register a HTTP client with theIHostBuilder:protected override void OnLaunched(LaunchActivatedEventArgs args) { var appBuilder = this.CreateBuilder(args) .Configure(hostBuilder => { hostBuilder.UseHttp(); }); ... }The
UseHttp()extension method accepts a callback for configuring the HTTP services as its argument. We will use this callback to register endpoints with the service collection.protected override void OnLaunched(LaunchActivatedEventArgs args) { var appBuilder = this.CreateBuilder(args) .Configure(hostBuilder => { hostBuilder.UseHttp((ctx, services) => { // Register endpoints here }); }); ... }ctxrepresents theHostBuilderContext. This can be used to access the configuration of the host.servicesis an instance ofIServiceCollection. This is used to register services with the host.
An extension method
AddClientWithEndpoint<TInterface, TEndpoint>()is included which allows specifying custom endpoint options when adding a typed client to the service collection.Use this extension method to register a typed client with the service collection and specify custom endpoint options of the type
CustomEndpointOptions.protected override void OnLaunched(LaunchActivatedEventArgs args) { var appBuilder = this.CreateBuilder(args) .Configure(hostBuilder => { hostBuilder.UseHttp((ctx, services) => { services.AddClientWithEndpoint<HttpEndpointsOneViewModel, CustomEndpointOptions>(); }); }); ... }Type parameter
TInterfaceis the service or view model interface that will be used to access the endpoint.Type parameter
TEndpointis the type of the custom endpoint options you define. This type must be a subclass ofEndpointOptions.
The extension method above allows you to pass arguments for various details such as the
HostBuilderContext, an endpoint name (which corresponds to a configuration section), and a callback for configuring theHttpClientassociated with this endpoint.Add this information to the method call as shown below:
protected override void OnLaunched(LaunchActivatedEventArgs args) { var appBuilder = this.CreateBuilder(args) .Configure(hostBuilder => { hostBuilder.UseHttp((ctx, services) => { services.AddClientWithEndpoint<HttpEndpointsOneViewModel, CustomEndpointOptions>( ctx, name: "HttpDummyJsonEndpoint", configure: (builder, options) => { builder.ConfigureHttpClient(client => { // Configure the HttpClient here }); } ); }); }); ... }We assigned the endpoint a name of
HttpDummyJsonEndpoint. This name corresponds to a configuration section in theappsettings.jsonfile. We will add this section in the next section.The
configurecallback is used to configure theHttpClientassociated with the endpoint.Tip
This callback is optional. If you do not need to configure the
HttpClient, you can omit this callback.Notice that the callback accepts two arguments:
builderandoptions.optionsis an instance ofCustomEndpointOptionswhich we defined earlier. We will use this to access the custom options you defined in the previous section.Add an
ApiKeyto the request headers on the client using theConfigureHttpClientmethod.protected override void OnLaunched(LaunchActivatedEventArgs args) { var appBuilder = this.CreateBuilder(args) .Configure(hostBuilder => { hostBuilder.UseHttp((ctx, services) => { services.AddClientWithEndpoint<HttpEndpointsOneViewModel, CustomEndpointOptions>( ctx, name: "HttpDummyJsonEndpoint", configure: (builder, options) => { builder.ConfigureHttpClient(client => { if (options?.ApiKey is not null) { client.DefaultRequestHeaders.Add("ApiKey", options.ApiKey); } }); } ); }); }); ... }The
ApiKeyheader is added to theHttpClientusing theDefaultRequestHeadersproperty.The value of the header is set to the
ApiKeyproperty of theCustomEndpointOptionsinstance.
We have successfully registered an endpoint with the service collection. We will now add a configuration section for this endpoint.
3. Adding a configuration section for the endpoint
Open the
appsettings.jsonfile and add a configuration section for the endpoint:{ "HttpDummyJsonEndpoint": { "Url": "https://DummyJson.com", "UseNativeHandler": true, "ApiKey": "FakeApiKey" } }The name of the configuration section must match the name of the endpoint you specified in the previous section.
The
Urlproperty is used to specify the URL of the endpoint.The
ApiKeyproperty is used to specify the API key that will be added to the request header.The
UseNativeHandlerproperty is used to explicitly specify whether to use the native HTTP handler.
4. Using the endpoint
We will now use the endpoint in a view model. Create and a
HttpEndpointsOneViewModelclass with a constructor that accepts an instance ofHttpClientlike so:public class HttpEndpointsOneViewModel { private readonly HttpClient _client; public string? Data { get; internal set;} public HttpEndpointsOneViewModel(HttpClient client) { _client = client; } public async Task Load() { Data = await _client.GetStringAsync("products"); } }- The
HttpClientinstance is injected into the view model. This instance is configured with the options we specified in the previous sections.
- The
All the details of
IHttpClientFactoryare abstracted away from the view model. The view model can simply use thisHttpClientinstance to make requests to the endpoint. The instance can have a managed lifecycle, while a significant amount of ceremony and unintuitive workarounds are avoided.