diff --git a/.gitignore b/.gitignore index 3331d2593d..7aca9eb407 100644 --- a/.gitignore +++ b/.gitignore @@ -334,3 +334,4 @@ ASALocalRun/ .vscode/**/*.* DebugPlatforms.props +.DS_Store \ No newline at end of file diff --git a/doc/Learn/Tutorials/Http/HowTo-EndpointOptions.md b/doc/Learn/Tutorials/Http/HowTo-EndpointOptions.md new file mode 100644 index 0000000000..d9bfd5caa9 --- /dev/null +++ b/doc/Learn/Tutorials/Http/HowTo-EndpointOptions.md @@ -0,0 +1,221 @@ +--- +uid: Learn.Tutorials.Http.HowToEndpointOptions +--- + +# 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](xref:Uno.GetStarted) set up for developing Uno Platform applications + +* The solution was created using the template wizard or `dotnet new unoapp`. See [Overview](xref:Overview.Extensions) + +* Basic conceptual understanding of accessing web resources using HTTP requests + +* Knowledge of how to [register an endpoint for HTTP requests](xref:Learn.Tutorials.Http.HowToHttp) + +## Step-by-steps + +> [!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 [instructions](xref:Overview.Extensions) for creating an application from the template. + +### 1. Preparing for custom endpoint options + +* Create a new class called `CustomEndpointOptions` in the shared project. This class extends `EndpointOptions` to allow you to specify custom options for the endpoint + + ```csharp + public 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 a `Url` property. This property is used to specify the URL of the endpoint. + + * Subclassing `EndpointOptions` will allow you to configure the `HttpClient` associated with the endpoint — all from a single configuration section. + +### 2. Defining the endpoint + +* Enable HTTP by calling the `UseHttp()` method to register a HTTP client with the `IHostBuilder`: + + ```csharp + 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. + + ```csharp + protected override void OnLaunched(LaunchActivatedEventArgs args) + { + var appBuilder = this.CreateBuilder(args) + .Configure(hostBuilder => + { + hostBuilder.UseHttp((ctx, services) => { + // Register endpoints here + }); + }); + ... + ``` + + * `ctx` represents the `HostBuilderContext`. This can be used to access the configuration of the host. + + * `services` is an instance of `IServiceCollection`. This is used to register services with the host. + +* An extension method `AddClientWithEndpoint()` 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`. + + ```csharp + protected override void OnLaunched(LaunchActivatedEventArgs args) + { + var appBuilder = this.CreateBuilder(args) + .Configure(hostBuilder => + { + hostBuilder.UseHttp((ctx, services) => { + services.AddClientWithEndpoint(); + }); + }); + ... + ``` + + * 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 of `EndpointOptions`. + +* 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 the `HttpClient` associated with this endpoint. + + * Add this information to the method call as shown below: + + ```csharp + protected override void OnLaunched(LaunchActivatedEventArgs args) + { + var appBuilder = this.CreateBuilder(args) + .Configure(hostBuilder => + { + hostBuilder.UseHttp((ctx, services) => { + services.AddClientWithEndpoint( + 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 the `appsettings.json` file. We will add this section in the next section. + + * The `configure` callback is used to configure the `HttpClient` 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` and `options`. `options` is an instance of `CustomEndpointOptions` 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 the `ConfigureHttpClient` method. + + ```csharp + protected override void OnLaunched(LaunchActivatedEventArgs args) + { + var appBuilder = this.CreateBuilder(args) + .Configure(hostBuilder => + { + hostBuilder.UseHttp((ctx, services) => { + services.AddClientWithEndpoint( + 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 the `HttpClient` using the `DefaultRequestHeaders` property. + + * The value of the header is set to the `ApiKey` property of the `CustomEndpointOptions` 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: + + ```json + { + "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 of `HttpClient` like so: + + ```csharp + 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. + +* All the details of `IHttpClientFactory` are abstracted away from the view model. The view model can simply use this `HttpClient` 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. + +## See also + +- [How-To: Register an Endpoint for HTTP Requests](xref:Learn.Tutorials.Http.HowToHttp) +- [How-To: Consume a web API with HttpClient](xref:Uno.Development.ConsumeWebApi) +- [How-To: Create a Strongly-Typed REST Client for an API](xref:Learn.Tutorials.Http.HowToRefit) +- [Overview: HTTP](xref:Overview.Http) +- [Overview: Polly and HttpClientFactory](https://github.com/App-vNext/Polly/wiki/Polly-and-HttpClientFactory) +- [Explore: TestHarness HTTP Endpoints](https://github.com/unoplatform/uno.extensions/tree/main/testing/TestHarness/TestHarness.Shared/Ext/Http/Endpoints) \ No newline at end of file diff --git a/doc/Learn/Tutorials/Http/HowTo-Http.md b/doc/Learn/Tutorials/Http/HowTo-Http.md index 6714a9dd72..b61f7506ed 100644 --- a/doc/Learn/Tutorials/Http/HowTo-Http.md +++ b/doc/Learn/Tutorials/Http/HowTo-Http.md @@ -86,5 +86,8 @@ When working with a complex application, centralized registration of your API en ## See also - [How-To: Consume a web API with HttpClient](xref:Uno.Development.ConsumeWebApi) -- [Polly and HttpClientFactory](https://github.com/App-vNext/Polly/wiki/Polly-and-HttpClientFactory) -- [How-To: Quickly Create a Strongly-Typed REST Client for an API](xref:Learn.Tutorials.Http.HowToRefit) \ No newline at end of file +- [How-To: Create a Strongly-Typed REST Client for an API](xref:Learn.Tutorials.Http.HowToRefit) +- [How-To: Configure with Custom Endpoint Options](xref:Learn.Tutorials.Http.HowToEndpointOptions) +- [Overview: HTTP](xref:Overview.Http) +- [Overview: Polly and HttpClientFactory](https://github.com/App-vNext/Polly/wiki/Polly-and-HttpClientFactory) +- [Explore: TestHarness HTTP Endpoints](https://github.com/unoplatform/uno.extensions/tree/main/testing/TestHarness/TestHarness.Shared/Ext/Http/Endpoints) \ No newline at end of file diff --git a/doc/Learn/Tutorials/Http/HowTo-Refit.md b/doc/Learn/Tutorials/Http/HowTo-Refit.md index da15660b1c..0b0f97baab 100644 --- a/doc/Learn/Tutorials/Http/HowTo-Refit.md +++ b/doc/Learn/Tutorials/Http/HowTo-Refit.md @@ -251,8 +251,11 @@ When accessing resources with a [REST-style](https://www.ics.uci.edu/~fielding/p ## See also -* [Use HttpClientFactory to implement resilient HTTP requests](https://learn.microsoft.com/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#benefits-of-using-ihttpclientfactory) -* [Microsoft.Extensions.Http](https://www.nuget.org/packages/Microsoft.Extensions.Http) -* [Refit](https://github.com/reactiveui/refit) -* [Hoppscotch](https://hoppscotch.io) -* [quicktype](https://app.quicktype.io/) +- [How-To: Consume a web API with HttpClient](xref:Uno.Development.ConsumeWebApi) +- [How-To: Register an Endpoint for HTTP Requests](xref:Learn.Tutorials.Http.HowToHttp) +- [Overview: HTTP](xref:Overview.Http) +- [Overview: Use HttpClientFactory to implement resilient HTTP requests](https://learn.microsoft.com/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#benefits-of-using-ihttpclientfactory) +- [Overview: What is Refit?](https://github.com/reactiveui/refit) +- [Explore: Hoppscotch tool](https://hoppscotch.io) +- [Explore: quicktype tool](https://app.quicktype.io/) +- [Explore: TestHarness Refit Endpoints](https://github.com/unoplatform/uno.extensions/tree/main/testing/TestHarness/TestHarness.Shared/Ext/Http/Refit) \ No newline at end of file diff --git a/doc/Overview/Http/HttpOverview.md b/doc/Overview/Http/HttpOverview.md index 946cf824a6..1bbea3d4c0 100644 --- a/doc/Overview/Http/HttpOverview.md +++ b/doc/Overview/Http/HttpOverview.md @@ -3,17 +3,35 @@ uid: Overview.Http --- # HTTP -Uno.Extensions.Http allows for the registration of API endpoints as multiple typed `HttpClient` instances. In this centralized location for accessing web resources, the lifecycle of the corresponding `HttpMessageHandler` objects is managed. Added clients can optionally be configured to use the platform-native handler. Additional functionality is provided to clear cookies or log diagnostic messages in responses. This library uses [Microsoft.Extensions.Http](https://www.nuget.org/packages/Microsoft.Extensions.Http) for any HTTP related work. +Uno.Extensions.Http allows for the registration of API **endpoints** as multiple typed `HttpClient` instances. In this centralized location for accessing web resources, the lifecycle of the corresponding `HttpMessageHandler` objects is managed. Added clients can optionally be configured to use the platform-native handler. Additional functionality is provided to clear cookies or log diagnostic messages in responses. This library uses [Microsoft.Extensions.Http](https://www.nuget.org/packages/Microsoft.Extensions.Http) for any HTTP related work. -For more documentation on HTTP requests, read the references listed at the bottom. +For additional documentation on HTTP requests, read the references listed at the bottom. ## Register Endpoints Web resources exposed through an API are defined in the application as clients. These client registrations include type arguments and endpoints to be used for the client. The endpoint is defined in the `EndpointOptions` class. While it uses the platform-native HTTP handler by default, this value can be configured. ```csharp -private IHost Host { get; } +protected override void OnLaunched(LaunchActivatedEventArgs args) +{ + var builder = this.CreateBuilder(args) + .Configure(host => { + host + .UseHttp((context, services) => + { + services + .AddClient(context, "configsectionname"); + }); + }); +... +``` + +> [!TIP] +> If configuration sections are already used elsewhere, continuing to use that approach offers uniformity and broader accessibility of endpoint options. Consider whether this type of access is needed before using the alternate method below. +`EndpointOptions` can also be loaded from a specific instance. + +```csharp protected override void OnLaunched(LaunchActivatedEventArgs args) { var builder = this.CreateBuilder(args) @@ -33,28 +51,28 @@ protected override void OnLaunched(LaunchActivatedEventArgs args) ... ``` -`EndpointOptions` can also be loaded from a specified configuration section name. Refer to the [Configuration](xref:Overview.Configuration) documentation for more information. +### Custom Endpoint Options -```csharp -private IHost Host { get; } +`EndpointOptions` is a base class that provides a `Url` property. This property is used to specify the URL of the endpoint. Subclassing `EndpointOptions` allows for custom options beyond the `Url` such as a proxy, timeout, and adding headers. Using this method, the `HttpClient` associated with the endpoint can be configured from a single section in `appsettings.json`. +```csharp protected override void OnLaunched(LaunchActivatedEventArgs args) { - var builder = this.CreateBuilder(args) - .Configure(host => { - host - .UseHttp((context, services) => - { - services - .AddClient(context, "configsectionname"); + var appBuilder = this.CreateBuilder(args) + .Configure(hostBuilder => + { + hostBuilder.UseHttp((ctx, services) => { + services.AddClientWithEndpoint(); }); }); ... ``` +See the [tutorial](xref:Learn.Tutorials.Http.HowToEndpointOptions) for more information about configuring `HttpClient` with custom endpoint options. + ## Refit -Refit endpoints can be configured as services in a similar way. +Similarly, **Refit endpoints** can be registered as services and configured in a similar way. ```csharp private IHost Host { get; } @@ -73,7 +91,7 @@ protected override void OnLaunched(LaunchActivatedEventArgs args) ... ``` -In this case, the `EndpointOptions` will be loaded from configuration section ChuckNorrisEndpoint. The configuration section could be defined as follows: +In this case, the endpoint options will be loaded from configuration section _ChuckNorrisEndpoint_ which can be defined as the following JSON: ```json { @@ -87,7 +105,13 @@ In this case, the `EndpointOptions` will be loaded from configuration section Ch See the [tutorial](xref:Learn.Tutorials.Http.HowToRefit) for more information on using Refit. ## References -- [Making HTTP requests using IHttpClientFactory](https://learn.microsoft.com/aspnet/core/fundamentals/http-requests) -- [Delegating handlers](https://learn.microsoft.com/aspnet/web-api/overview/advanced/http-message-handlers) -- [Polly and HttpClientFactory](https://github.com/App-vNext/Polly/wiki/Polly-and-HttpClientFactory) -- [What is Refit?](https://github.com/reactiveui/refit) \ No newline at end of file + +- [How-To: Consume a web API with HttpClient](xref:Uno.Development.ConsumeWebApi) +- [How-To: Register an Endpoint for HTTP Requests](xref:Learn.Tutorials.Http.HowToHttp) +- [How-To: Configure with Custom Endpoint Options](xref:Learn.Tutorials.Http.HowToEndpointOptions) +- [How-To: Create a Strongly-Typed REST Client for an API](xref:Learn.Tutorials.Http.HowToRefit) +- [Overview: Use HttpClientFactory to implement resilient HTTP requests](https://learn.microsoft.com/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#benefits-of-using-ihttpclientfactory) +- [Overview: Delegating handlers](https://learn.microsoft.com/aspnet/web-api/overview/advanced/http-message-handlers) +- [Overview: Polly and HttpClientFactory](https://github.com/App-vNext/Polly/wiki/Polly-and-HttpClientFactory) +- [Overview: What is Refit?](https://github.com/reactiveui/refit) +- [Explore: TestHarness HTTP](https://github.com/unoplatform/uno.extensions/tree/main/testing/TestHarness/TestHarness.Shared/Ext/Http/) \ No newline at end of file diff --git a/doc/toc.yml b/doc/toc.yml index 38c73226fa..6e3c17d311 100644 --- a/doc/toc.yml +++ b/doc/toc.yml @@ -50,6 +50,8 @@ href: Learn/Tutorials/Http/HowTo-Http.md - name: Use Refit to call a REST API href: Learn/Tutorials/Http/HowTo-Refit.md + - name: Configure with Custom Endpoint Options + href: Learn/Tutorials/Http/HowTo-EndpointOptions.md - name: Localization items: - name: Overview diff --git a/src/Uno.Extensions.Http.Refit/ServiceCollectionExtensions.cs b/src/Uno.Extensions.Http.Refit/ServiceCollectionExtensions.cs index 01ee9fdbf5..44a8676219 100644 --- a/src/Uno.Extensions.Http.Refit/ServiceCollectionExtensions.cs +++ b/src/Uno.Extensions.Http.Refit/ServiceCollectionExtensions.cs @@ -17,6 +17,7 @@ public static class ServiceCollectionExtensions /// The Refit api type to register /// The services collection to register the api with /// The host builder context + /// [optional] Endpoint information (loaded from appsettings if not specified) /// [optional]The name for locating endpoint information in appsettings /// [optional]Callback for overriding Refit settings /// [optional]Callback for configuring the endpoint @@ -24,14 +25,40 @@ public static class ServiceCollectionExtensions public static IServiceCollection AddRefitClient( this IServiceCollection services, HostBuilderContext context, + EndpointOptions? options = null, string? name = null, Action? settingsBuilder = null, Func? configure = null ) where TInterface : class + => services.AddRefitClientWithEndpoint(context, options, name, settingsBuilder, configure); + + /// + /// Registers a Refit client with the specified . + /// + /// The Refit api type to register + /// The type of endpoint to register + /// The services collection to register the api with + /// The host builder context + /// [optional] Endpoint information (loaded from appsettings if not specified) + /// [optional]The name for locating endpoint information in appsettings + /// [optional]Callback for overriding Refit settings + /// [optional]Callback for configuring the endpoint + /// + public static IServiceCollection AddRefitClientWithEndpoint( + this IServiceCollection services, + HostBuilderContext context, + TEndpoint? options = null, + string? name = null, + Action? settingsBuilder = null, + Func? configure = null + ) + where TInterface : class + where TEndpoint : EndpointOptions, new() { - return services.AddClient( + return services.AddClientWithEndpoint( context, + options, name: name, httpClientFactory: (s, c) => Refit.HttpClientFactoryExtensions.AddRefitClient(s, settingsAction: serviceProvider => { diff --git a/src/Uno.Extensions.Http/ServiceCollectionExtensions.cs b/src/Uno.Extensions.Http/ServiceCollectionExtensions.cs index 0aa6e0d266..c1fc1b0398 100644 --- a/src/Uno.Extensions.Http/ServiceCollectionExtensions.cs +++ b/src/Uno.Extensions.Http/ServiceCollectionExtensions.cs @@ -35,13 +35,37 @@ public static IServiceCollection AddClient( ) where TClient : class where TImplementation : class, TClient + => services.AddClientWithEndpoint(context, options, name, configure); + + /// + /// Adds a typed client to the service collection. + /// + /// The type of client to add + /// The type implementation + /// The type of endpoint to register + /// The service collection to register with + /// The host builder context + /// [optional] Endpoint information (loaded from appsettings if not specified) + /// [optional] Name of the endpoint (used to load from appsettings) + /// [optional] Callback to configure the endpoint + /// Updated service collection + public static IServiceCollection AddClientWithEndpoint( + this IServiceCollection services, + HostBuilderContext context, + TEndpoint? options = null, + string? name = null, + Func? configure = null + ) + where TClient : class + where TImplementation : class, TClient + where TEndpoint : EndpointOptions, new() { Func httpClientFactory = (s, c) => (name is null || string.IsNullOrWhiteSpace(name)) ? s.AddHttpClient() : s.AddHttpClient(name); - return services.AddClient(context, options, name, httpClientFactory, configure); + return services.AddClientWithEndpoint(context, options, name, httpClientFactory, configure); } /// @@ -64,9 +88,33 @@ public static IServiceCollection AddClient( Func? configure = null ) where TInterface : class + => services.AddClientWithEndpoint(context, options, name, httpClientFactory, configure); + + /// + /// Adds a typed client to the service collection. + /// + /// The type of client to add + /// The type of endpoint to register + /// The service collection to register with + /// The host builder context + /// [optional] Endpoint information (loaded from appsettings if not specified) + /// [optional] Name of the endpoint (used to load from appsettings) + /// [optional] Callback to configure the HttpClient + /// [optional] Callback to configure the endpoint + /// Updated service collection + public static IServiceCollection AddClientWithEndpoint( + this IServiceCollection services, + HostBuilderContext context, + TEndpoint? options = null, + string? name = null, + Func? httpClientFactory = null, + Func? configure = null + ) + where TInterface : class + where TEndpoint : EndpointOptions, new() { var optionsName = name ?? (typeof(TInterface).IsInterface ? typeof(TInterface).Name.TrimStart(InterfaceNamePrefix) : typeof(TInterface).Name); - options ??= ConfigurationBinder.Get(context.Configuration.GetSection(optionsName)); + options ??= ConfigurationBinder.Get(context.Configuration.GetSection(optionsName)); httpClientFactory ??= (s, c) => (name is null || string.IsNullOrWhiteSpace(name)) ? diff --git a/testing/TestHarness/TestHarness.Shared/Ext/Http/Endpoints/CustomEndpointOptions.cs b/testing/TestHarness/TestHarness.Shared/Ext/Http/Endpoints/CustomEndpointOptions.cs new file mode 100644 index 0000000000..dc2208f103 --- /dev/null +++ b/testing/TestHarness/TestHarness.Shared/Ext/Http/Endpoints/CustomEndpointOptions.cs @@ -0,0 +1,8 @@ +using Uno.Extensions.Http; + +namespace TestHarness.Ext.Http.Endpoints; + +public class CustomEndpointOptions: EndpointOptions +{ + public string? ApiKey { get; set; } +} diff --git a/testing/TestHarness/TestHarness.Shared/Ext/Http/Endpoints/HttpEndpointsHostInit.cs b/testing/TestHarness/TestHarness.Shared/Ext/Http/Endpoints/HttpEndpointsHostInit.cs index c1bb169194..1813e5c1b6 100644 --- a/testing/TestHarness/TestHarness.Shared/Ext/Http/Endpoints/HttpEndpointsHostInit.cs +++ b/testing/TestHarness/TestHarness.Shared/Ext/Http/Endpoints/HttpEndpointsHostInit.cs @@ -11,7 +11,18 @@ protected override IHostBuilder Custom(IHostBuilder builder) return builder .UseHttp( configure: (context, services) => - services.AddClient(context, name: "HttpDummyJsonEndpoint")); + services.AddClientWithEndpoint( + context, + name: "HttpDummyJsonEndpoint", + configure: (builder, options) => + builder.ConfigureHttpClient(client => + { + if (options?.ApiKey is not null) + { + client.DefaultRequestHeaders.Add("ApiKey", options.ApiKey); + } + }) + )); } diff --git a/testing/TestHarness/TestHarness.Shared/Ext/Http/Endpoints/appsettings.httpendpoints.json b/testing/TestHarness/TestHarness.Shared/Ext/Http/Endpoints/appsettings.httpendpoints.json index ae65b739e7..50f374a7c7 100644 --- a/testing/TestHarness/TestHarness.Shared/Ext/Http/Endpoints/appsettings.httpendpoints.json +++ b/testing/TestHarness/TestHarness.Shared/Ext/Http/Endpoints/appsettings.httpendpoints.json @@ -1,6 +1,7 @@ { "HttpDummyJsonEndpoint": { "Url": "https://DummyJson.com", - "UseNativeHandler": true + "UseNativeHandler": true, + "ApiKey": "FakeApiKey" } } diff --git a/testing/TestHarness/TestHarness.Shared/Ext/Http/Refit/HttpRefitHostInit.cs b/testing/TestHarness/TestHarness.Shared/Ext/Http/Refit/HttpRefitHostInit.cs index 207b76f467..f5828fdb70 100644 --- a/testing/TestHarness/TestHarness.Shared/Ext/Http/Refit/HttpRefitHostInit.cs +++ b/testing/TestHarness/TestHarness.Shared/Ext/Http/Refit/HttpRefitHostInit.cs @@ -1,4 +1,5 @@ -using TestHarness.Ext.Http.Refit; +using TestHarness.Ext.Http.Endpoints; +using TestHarness.Ext.Http.Refit; namespace TestHarness; @@ -11,7 +12,18 @@ protected override IHostBuilder Custom(IHostBuilder builder) return builder .UseHttp( configure: (context, services) => - services.AddRefitClient(context, name: "HttpRefitDummyJsonEndpoint")); + services.AddRefitClientWithEndpoint( + context, + name: "HttpRefitDummyJsonEndpoint", + configure: (builder, options) + => builder.ConfigureHttpClient(client => + { + if (options?.ApiKey is not null) + { + client.DefaultRequestHeaders.Add("ApiKey", options.ApiKey); + } + }) + )); } diff --git a/testing/TestHarness/TestHarness.Shared/Ext/Http/Refit/appsettings.httprefit.json b/testing/TestHarness/TestHarness.Shared/Ext/Http/Refit/appsettings.httprefit.json index e48de30890..be042e7a27 100644 --- a/testing/TestHarness/TestHarness.Shared/Ext/Http/Refit/appsettings.httprefit.json +++ b/testing/TestHarness/TestHarness.Shared/Ext/Http/Refit/appsettings.httprefit.json @@ -1,6 +1,7 @@ { "HttpRefitDummyJsonEndpoint": { "Url": "https://DummyJson.com", - "UseNativeHandler": true + "UseNativeHandler": true, + "ApiKey": "FakeApiKey" } } diff --git a/testing/TestHarness/TestHarness.Shared/TestHarness.Shared.projitems b/testing/TestHarness/TestHarness.Shared/TestHarness.Shared.projitems index 12b6469531..345c56cf36 100644 --- a/testing/TestHarness/TestHarness.Shared/TestHarness.Shared.projitems +++ b/testing/TestHarness/TestHarness.Shared/TestHarness.Shared.projitems @@ -101,6 +101,7 @@ StorageOnePage.xaml + HttpEndpointsMainPage.xaml @@ -748,7 +749,6 @@ - \ No newline at end of file