C# HttpClient refresh token strategy

Since Microsoft recommends that the HttpClient be created once and reused throughout the life of a program, I wondering how to update DefaultRequestHeaders when for example, the token has expired and need to be refresh.

DefaultRequestHeaders is more over not thread safe (for what I know) and the list of headers defined there, shared by all potentially pending requests. Clear() the list and Add() the header with a new token, seems not the wise thing to do.


Update

To be more precise, I don't want/need to change request headers for every request. Only when I've got a HTTP 401 status code.

Upvotes: 13

Views: 8286

Answers (1)

Brennan Mann
Brennan Mann

Reputation: 1537

Wire up a message handler with your HttpClient when you register the IHttpClient in the DI container registry phase or use another pattern such as a factory or singleton to return an instance of the IHttpClient with a custom message handler. Inspect the outbound call and add the necessary headers.

https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/httpclient-message-handlers

Sample header message handler

class MessageHandler1 : DelegatingHandler
    {


    private int _count = 0;

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        System.Threading.Interlocked.Increment(ref _count);
        request.Headers.Add("X-Custom-Header", _count.ToString());
        return base.SendAsync(request, cancellationToken);
    }
}

Sample logger message handler:

class LoggingHandler : DelegatingHandler

{
    StreamWriter _writer;

public LoggingHandler(Stream stream)
{
    _writer = new StreamWriter(stream);
}

protected override async Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
    var response = await base.SendAsync(request, cancellationToken);

    if (!response.IsSuccessStatusCode)
    {
        _writer.WriteLine("{0}\t{1}\t{2}", request.RequestUri, 
            (int)response.StatusCode, response.Headers.Date);
    }
    return response;
}

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        _writer.Dispose();
    }
    base.Dispose(disposing);
}

}

Add it to the pipeline

HttpClient client = HttpClientFactory.Create(new Handler1(), new Handler2(), new Handler3());

Threading Concerns

Regarding threading concerns or concurrency, the HttpRequestMessage parameter on the SendAsync method will be per request. If you add the header to the request.Headers collection, you will updating headers for that instance of the request only (i.e., not globally )

Or use the Authorization property on the request.Headers instance:

request.Headers.Authorization = new AuthenticationHeaderValue("bearer", bearerToken);

Please see MSDN link below

https://msdn.microsoft.com/en-us/library/system.net.http.httprequestmessage

If you use DefaultRequestHeaders on a static, shared, singleton, Lifestyle.Singleton, etc, instance of the HttpClient then you will have threading concerns and need proper synchronization to update the DefaultRequestHeaders collection.

Upvotes: 7

Related Questions