Using Steeltoe DiscoveryHttpMessageHandler with FlurlClient - c#

I am looking to switch our HttpClients to use Flurl. However, our HttpClient is currently configured to use Service Discovery via Steeltoe. Basically it's doing this in ConfigureServices:
services.AddHttpClient<IMyClass, MyClass>().AddHttpMessageHandler<DiscoveryHttpMessageHandler>();
DiscoveryHttpMessageHandler is a custom http message handler in the Steeltoe library (https://github.com/SteeltoeOSS)
How do I access the IHttpClientBuilder with Flurl so I can add this same message hander? Or is there another clean way with Flurl to add a custom message handler for every HttpClient/FlurlClient created?

There's a few ways to add a custom message handler with Flurl (such as with a custom factory), but since you're already using IHttpClientFactory, I think the easiest path to get what you want (and the one I'd recommend) is to continue injecting HttpClient into your services as you're doing and wrap them with Flurl inside the service:
public class MyClass : IMyClass
{
private readonly IFlurlClient _flurlClient;
public MyService(HttpClient httpClient) {
_flurlClient = new FlurlClient(httpClient);
}
}

Related

How to implement a common Authentication Handler for different Http Clients in .NET 6?

I am currently refactoring an old written .NET API, after migrating it to .NET 6. There are 5 different third party API calls that are made from this API to gather data.
We have used Refit to write interfaces for each of these 5 clients. Now we need to implement an authentication handler which adds headers for Service Mesh Authentication. I am planning to write a delegating handler but each of these 5 clients might use different values. I am not sure how to handle this while adding the common handler using Dependency Injection. Please refer the code below:
//Client 1
services.AddRefitClient<IExternalHttpClient1>()
.ConfigureHttpClient(client => client.BaseAddress = new Uri(Configuration["UrlKey"]))
.AddHttpMessageHandler<ServiceMeshAuthHeaderHandler>();
//Client 2
services.AddRefitClient<IExternalHttpClient2>()
.ConfigureHttpClient(client => client.BaseAddress = new Uri(Configuration["UrlKey"]))
.AddHttpMessageHandler<ServiceMeshAuthHeaderHandler>();
ServiceMeshAuthHeaderHandler is the code which generates header for authentication for a client. The code to generate header will be same but the header value will differ based on the which client we want to call.
public class ServiceMeshAuthHeaderHandler : DelegatingHandler
{
private readonly ServiceMeshConfiguration _serviceMeshConfiguration;
private readonly IRSACryptoServiceManager _rsaCryptoServiceManager;
private readonly ILogger<RSACryptoServiceManager> _logger;
......
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
// Based on values in _serviceMeshConfiguration, generate headers for a client
return await base.SendAsync(request, cancellationToken);
}
}
I know I am missing something very trivial here but right now the best solution is not clicking to me. One approach can be to derive 5 classes for each client from ServiceMeshAuthHeaderHandler, each having its unique _serviceMeshConfiguration for the respective client and then call the base abstract class method having common functionality. But I am not sure if this is the optimal solution. Any suggestions ?

Add Jwt token in header for every Http call using the concept of `HttpInterceptor` From angular for asp.net core web api [duplicate]

This question already has answers here:
Configure HttpClientFactory to use data from the current request context
(2 answers)
Closed 7 months ago.
Is there any way to implement an interceptor so that I can call a microservice api from another microservices where all the http call will be intercepted & added jwt token in the header? So that, I don't have to set authorization token each time I request.
In angular there is a concept of HttpInterceptor. Where each http requests will be intercepted ans added jwt token in the header. Is there any way to achieve that in asp.net core web api?
A simple approach are named clients. Here you add a preconfigured HttpClient instance to your service registry and can access this instance wherever you need it:
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");
});
Use the httpClient.Default* properties.
See: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-6.0#named-clients
The drawback is, that you don't know how requests must look like when using the instance. One approach to solve this is to create a so-called typed client. Basically a wrapper around HttpClient adding the right configuration and enforcing the correct request and response types.
public class CatalogService : ICatalogService
{
private readonly HttpClient _httpClient;
private readonly string _remoteServiceBaseUrl;
public CatalogService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<Catalog> GetCatalogItems(int page, int take, int? brand, int? type)
{
var uri = API.Catalog.GetAllCatalogItems(_remoteServiceBaseUrl, page, take, brand, type);
var responseString = await _httpClient.GetStringAsync(uri);
var catalog = JsonConvert.DeserializeObject<Catalog>(responseString);
return catalog;
}
}
(example from the docs here: https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests)
If you have static values (e.g. a fixed client secret), you can use options to inject them as described here: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-6.0
If you get your token from another service, obviously just inject this other service.
You can also configure the HttpClient when adding your typed client to the service registry:
services.AddHttpClient<ICatalogService, CatalogService>(client =>
{
client.BaseAddress = new Uri(Configuration["BaseUrl"]);
});
Example from: https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#multiple-ways-to-use-ihttpclientfactory
Configuring the client here is similar to angular injectors in the way, that it separates configuration and client implementation. Example: You could use the same CatalogService class with a catalog server that expects an Authorization header and a server that expects "MySpecialAuth" header without having to take care of those differences in the CatalogService class.
To show the differences in the flow...
Angular iterceptors: use shared http client > then intercept requests based on e.g. domain
C# clients: preconfigure clinet > then use configured instance
Side notes:
You typically should not create new instances of HttpClient manually. Inject them using and use IHttpClientFactory in the background.
You can create a custom HttpClient using inheritence which adds this Header in each request, and use this across your application

Middleware add new dependency to DI Container or Services

I am writing a piece of middleware (maybe I want a scoped service??), I guess my plan is to have some kind of multi-tenant scenario.
If for example, I have 2 domains that respond on this service:
www.domain1.com
www.domain2.com
I want to capture the request when it starts, look at the host name that is being used and then set some other object to be available through Dependency Injection for everything further up the pipeline.
It seems that middleware should be the right way to achieve this, but not sure how to do the final step.
My options seem to be:
Middleware
Register Singleton service to access database
Register early to be the first item of middleware to capture the request.
Analyse Request Object and build custom configuration object
Add custom configuration as a scoped object to the DI container for use by other services
Service
Register Singleton service to access database
Register Singleton service for IHttpContextAccessor
Register Scoped? Service - to do equivalent of middleware
Analyse the request object and build custom configuration object
Register custom object as new scoped object in the DI container
My assumption is that the Service is able to register the custom scoped object as it is still within the ConfigureServices method of the startup.cs
However, with middleware it is initialised through the Configure method by which point the DI container has already been built?
You can use the factory-overload of AddScoped for the service you want to be different per tenant/request. Here's an example:
services.AddScoped<IServiceForTenant>(sp =>
{
var httpContextAccessor = sp.GetRequiredService<IHttpContextAccessor>();
var serviceForTenant = new ServiceForTenant();
// TODO: Use httpContextAcccessor.HttpContext to configure serviceForTenant.
return serviceForTenant;
});
For each request that comes in to your ASP.NET Core application, the code above will run when you first request IServiceForTenant in e.g. a controller. At this point, your code can read from IHttpContextAccessor.HttpContext and make whatever decisions it needs in order to create the implementation instance for IServiceForTenant. This same instance will then be used for the rest of the request (i.e. further up the pipeline).
The argument passed into AddScoped is Func<IServiceProvider, T>. All you need to provide here is a delegate of some kind, which could be done in one of many ways. Here's some examples:
You could just wrap the call into its own extension method, like this:
public static void AddServiceForTenant(this IServiceCollection services)
{
services.AddScoped<IServiceForTenant>(sp =>
{
// ...
});
}
In ConfigureServices:
services.AddServiceForTenant();
Use a class with a static method:
public static class ServiceForTenantFactory
{
public static ITenantForService Create(IServiceProvider sp)
{
// ...
}
}
In ConfigureServices:
services.AddScoped(ServiceForTenantFactory.Create);
Use a class with an instance method:
public class ServiceForTenantFactory
{
public ITenantForService Create(HttpContext httpContext)
{
// ...
}
}
In ConfigureServices:
services.AddScoped(sp =>
{
var httpContextAccessor = sp.GetRequiredService<IHttpContextAccessor>();
var serviceForTenantFactory = new ServiceForTenantFactory(); // Or use DI.
return serviceForTenantFactory.Create(httpContextAccessor.HttpContext);
});
This last option is the most flexible, as you could even resolve ServiceForTenantFactory itself from DI and it can have its own dependencies, etc. Note also that Create here takes the HttpContext directly (as an example).
As I've already said, there are yet more options than the three of shown, but this should be a good base to work with.

Create multiple instances of same dependency w/ different configuration

I have an application (IJobInit) that uses a list from JSON settings to create multiple instances of a class (IJob). This class does some work using two other dependencies, IInputClient and IOutputClient. It uses M.Extensions.DependencyInjection to create a container which is handed off to AutoFac to create an IContainer.
IJobInit(IContainer container)
I would like IInputClient to be configured different for each instance of IJob. Speficially, I'd like to pass in a secret for it to use. The result would be:
IInputClient(HttpClient client)
where HttpClient is configured using ConfigureHttpClient such that IJob does not know that it is pre-authenticated. This would also be suitable:
IInputClient(ISecretProvider secretsProvider, string secretName)
The end result is three instances of IJob with IInputClient configured differently.
IJob(IInputClient inputClient1, IOutputClient outputClient)
IJob(IInputClient inputClient2, IOutputClient outputClient)
IJob(IInputClient inputClient3, IOutputClient outputClient)
How do I achieve this? I was looking at Autofac scopes but those controlwhen an instance is created without any control over its configuration (unless I missed it).
A colleague suggested that I could host each instance of IJob in its own process with its own configuration which is possible but I'm trying to host all the jobs in a single Azure Function and use the list in config to create the inner jobs.
Thanks!
I'm not totally happy with this solution but it works for now.
private async Task<IInputClient> GetClientAsync(string secretId)
{
HttpClient httpClient = this.httpClientFactory.CreateClient();
string secret = await this.secretsProvider.GetSecretAsync(secretId);
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Concat(":", secret))));
return this.scope.Resolve<IInputClient>(new TypedParameter(typeof(HttpClient), httpClient));
}

How to use WinHttpHandler with IHttpClientFactory in core2.1?

I'm trying to use the new IHttpClientFactory with an ASP.net Core 2.1 WEB API application.
public void ConfigureServices(IServiceCollection services)
{
// other services configuration
services.AddHttpClient();
}
In my ConfigureServices method, I don't see a way to add the IHttpClientFactoryand configure it to use WinHttpHandler.
The AddHttpClient methods that return a IHttpClientBuilder gives you access to methods that configure the HttpMessageHandler but those have to be derived from DelegatingHandler but WinHttpHandler does not derive from DelegatingHandler.
See no way to tell HttpClient to use WinHttpHandler when being constructed.
Figured it out.
Thanks to tip given by #Nkosi in the comments!
I solved this by using a named HttpClient when registering the HttpClient service and then configuring the message handler to use WinHttpHandler as the PrimaryHandler
services.AddHttpClient<HttpClient>("WinHttp")
.ConfigureHttpMessageHandlerBuilder(c =>
{
c.PrimaryHandler = new WinHttpHandler() { WindowsProxyUsePolicy = WindowsProxyUsePolicy.UseWinInetProxy };
});
Then when using the IHttpClientFactory, specify the name you gave when registering it.
var httpClient = this._httpClientFactory.CreateClient("WinHttp");
Your HttpClient will now use WinHttpHandler!
NOTE
To use WinHttpHandler you must add nuget package System.Net.Http.WinHttpHandler

Categories

Resources