13

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.

  • Don't use the helper methods, build an `HttpRequestMessage` and use `SendAsync` instead. – ProgrammingLlama Apr 21 '18 at 07:51
  • Thinking about it, you could maybe write an HttpClientHandler that automatically adds headers depending on where the request is going. – ProgrammingLlama Apr 21 '18 at 07:58
  • @john Use `SendAsync` or `HttpClientHandler` will not help to change header **only** when authorization fails after a token has expired for example. Besides a `DelegatingHandler` as pointed out by @brennan-mann seems more appropriate – Philippe Lécaillon Apr 21 '18 at 09:20
  • 1
    In the code sample below, there is a message handler that looks at the response. You could look at the HttpStatus and check for a 401 response, update your token and resubmit. Does your token have an expiration or time to live property that you can check before sending the request? – Brennan Mann Apr 21 '18 at 09:33
  • @PhilippeLécaillon I wasn't talking about changing the header, I was talking about moving away from `DefaultRequestHeaders` altogether. I see brennan went with my second idea :) – ProgrammingLlama Apr 21 '18 at 09:40
  • @BrennanMann No TTL or expiredIn property in my 'not so a token in fact' :) But implement a `DelegatingHandler` that requests a *token* when the `base.SendAsync()` fails with a 401 status is the way I'll go. I'll update my post with an example when I can. Thx – Philippe Lécaillon Apr 21 '18 at 09:46
  • Microsoft's current recommendation, as of mid-2019, in ASP.NET Core web-applications is to use `IHttpClientFactory` (which maintains a long-life'd pool of host platform-specific `HttpMessageHandler` instances) to create short-life'd `HttpClient` objects. This approach is not necessarily applicable to desktop and mobile applications, however and a singleton `HttpClient` _may_ be more appropriate. YMMV. – Dai Aug 25 '19 at 00:31

1 Answers1

7

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.

Brennan Mann
  • 1,467
  • 2
  • 17
  • 26