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
CustomEndpointOptions
in the shared project. This class extendsEndpointOptions
to 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
ApiKey
property will be used to store the token.The
EndpointOptions
class is a base class that provides aUrl
property. This property is used to specify the URL of the endpoint.Subclassing
EndpointOptions
will allow you to configure theHttpClient
associated with the endpoint — all from a single configuration section.
2. Defining the endpoint
Add
Http
to 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 }); }); ... }
ctx
represents theHostBuilderContext
. This can be used to access the configuration of the host.services
is 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
TInterface
is the service or view model interface that will be used to access the endpoint.Type parameter
TEndpoint
is 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 theHttpClient
associated 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.json
file. We will add this section in the next section.The
configure
callback is used to configure theHttpClient
associated 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:
builder
andoptions
.options
is an instance ofCustomEndpointOptions
which we defined earlier. We will use this to access the custom options you defined in the previous section.Add an
ApiKey
to the request headers on the client using theConfigureHttpClient
method.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
ApiKey
header is added to theHttpClient
using theDefaultRequestHeaders
property.The value of the header is set to the
ApiKey
property of theCustomEndpointOptions
instance.
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.json
file 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
Url
property is used to specify the URL of the endpoint.The
ApiKey
property is used to specify the API key that will be added to the request header.The
UseNativeHandler
property 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
HttpEndpointsOneViewModel
class with a constructor that accepts an instance ofHttpClient
like 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
HttpClient
instance is injected into the view model. This instance is configured with the options we specified in the previous sections.
- The
All the details of
IHttpClientFactory
are abstracted away from the view model. The view model can simply use thisHttpClient
instance to make requests to the endpoint. The instance can have a managed lifecycle, while a significant amount of ceremony and unintuitive workarounds are avoided.