9

How should I use HttpClientFactory to return an instance of HttpClient whose uri and credentials are determined at the point of the call?

The existing code looks like this:

var httpClientHandler = new HttpClientHandler()
    {
        Credentials = new NetworkCredential(userName, password),
    };
HttpClient client = new HttpClient(httpClientHandler);
client.BaseAddress = new Uri(_appSetting.ServiceURI);

3 Answers 3

16

your ConfigureServices method in Start up class

services.AddHttpClient("github", c =>
{
    //c.BaseAddress = new Uri("https://api.github.com/");
    c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
    c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
    
}).ConfigurePrimaryHttpMessageHandler(() =>
{
    return new HttpClientHandler()
    {
        UseDefaultCredentials = true,
        Credentials = new NetworkCredential("", ""),
    };
});

Your Controller will look like this

private readonly IHttpClientFactory _httpClientFactory;

public DataProController(IHttpClientFactory httpClientFactory)
{
    _httpClientFactory = httpClientFactory;
}

[HttpGet]
public async Task<ActionResult> Get()
{            
    var client = _httpClientFactory.CreateClient("github");
    
    client.BaseAddress = new Uri("https://api.github.com/");

    string result = await client.GetStringAsync("/");
    return Ok(result);
}

You may not be able to set up Network Credentials at the run time when using httpclientfactory and may need to setup up in the startup class. you can find about this issue here. https://github.com/aspnet/HttpClientFactory/issues/71

Sign up to request clarification or add additional context in comments.

1 Comment

install nuget package Microsoft.Extensions.Http and ConfigurePrimaryHttpMessageHandler chains after AddHttpClient()
10

You can create an authentication delegating handler like this:

public class AuthenticationHttpMessageHandler : DelegatingHandler
    {
        protected override async Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request,
            CancellationToken cancellationToken)
        {
            // Get the token or other type of credentials here
            // string scheme = ... // E.g. "Bearer", "Basic" etc.
            // string credentials = ... // E.g. formatted token, user/password etc.

            request.Headers.Authorization =
                new AuthenticationHeaderValue(scheme, credentials);

            return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
        }
    }

then add it to the HttpClient builder and to the DI container:

 services
     .AddTransient<AuthenticationHttpMessageHandler>()
     .AddHttpClient("MyClient")
     .AddHttpMessageHandler<AuthenticationHttpMessageHandler>();

then use IHttpClientFactory to create HttpClient instances.

The core advantage of this approach is that you clearly separate concerns. You don't touch the primary handler, you don't manage client creation manually, you utilize the whole power of the factory and its builder extension methods. The authentication handler is naturally injected in the pipeline and adds authorization to each request. This handler can be enhanced further by abstracting away the source of credentials and make the handler depend on some IAuthenticationProvider abstraction, which will require only DI configuration and not touching the HttpClient configuration code.

3 Comments

Seems the cleaner and most reusable solution to me. I assume the AuthenticationHttpMessageHandler can be shared with others typed HttpClients, is that correct?
@kerzerk yes. In that case you should add the handler to the services collection (services.AddTransient<AuthenticationHttpMessageHandler>()) only once, and will need to specify the handler to add to a client (.AddHttpMessageHandler<AuthenticationHttpMessageHandler>()) for each client.
Also, if what you're interested in is only OAuth2/OpenIdConnect authentication/authorization tokens, I'd advise using famous IdentityModel library (github.com/IdentityModel/IdentityModel), which adds its message handler to an HttpClient in a similar way. The advantage of that library is that it handles many tricky things of mentioned protocols like token caching and refreshing.
2

If you using the .net Dependency Injection you can add the configuration for one class into your setup code:

services
    .AddTransient<DataLoader>()
    .AddHttpClient<DataLoader>().ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler()
{
    Credentials = new NetworkCredential(LoadUsernameFromConfig(), LoadPasswordFromSecureLocation())
});

Add now the DI will inject a HttpClient that uses this credential into the DataLoader class:

    public class DataLoader
    {
        private readonly HttpClient httpClient;

        public DataLoader(HttpClient httpClient)
        {
            this.httpClient = httpClient;
        }

        public async Task LoadData(string tableName)
        {
            var json = await httpClient.GetStringAsync("https://protected.example.com/json");
            ...
        }
    }

(I would not be able to come up with this code if I had not the answer from Imran Arshad: Thanks!)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.