mahesh_ing
mahesh_ing

Reputation: 429

Updating custom header value added as DefaultRequestHeaders of HttpClient

I have a static httpclient shared across requests and I want to add one custom header to it.

httpClient.DefaultRequestHeaders.Add("customHeader", somevalue.ToString());

But I noticed that on every request the value is getting added to that header which I intend to replace on each request. I try to remove the header if it is already exist and add again but it gives me an errors on load test.

if (httpClient.DefaultRequestHeaders.Contains("customHeader"))
        {
            httpClient.DefaultRequestHeaders.Remove("customHeader");
        }
httpClient.DefaultRequestHeaders.Add("customHeader",somevalue.ToString());

Errors -

System.ArgumentException: An item with the same key has already been added.
System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
System.ArgumentNullException: Value cannot be null.

How can I update the custom header value on each request?

Upvotes: 22

Views: 27876

Answers (4)

nimblebit
nimblebit

Reputation: 559

When deciding how to assign Headers to the HttpClient consider if the Headers will be the same for every request especially if the HttpClient instance is shared across all threads.

If the Headers will always be the same the DefaultRequestHeaders can be used and only needs to be assigned once.

Any changes made to DefaultRequestHeaders will affect all threads accessing the instance.

The example below is for .NET Core and .NET 7

// include dependencies
using System.Linq;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

// check header has not already been set
if (!_httpClient.DefaultRequestHeaders.Contains("key"))
{
    _httpClient.DefaultRequestHeaders.Add("key", "value");
}

// test value exists
var headerValue = string.Empty;
    
if (_httpClient.DefaultRequestHeaders.Contains("key"))
{
    headerValue = _httpClient.DefaultRequestHeaders.GetValues("key").First();
}

// any other configuration...

// invoke the request using GetAsync
var response = await _httpClient.GetAsync("path");

var returnValue = string.Empty;

if (response.IsSuccessStatusCode)
{
    returnValue = await response.Content.ReadAsStringAsync();
}

However if the Headers are expected to change for each message, then instead setup a HttpRequestMessage instance while initializing each new request with the required values.

// include dependencies
using System.Linq;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

// use shared single instance to avoid port exhaustion in constructor etc.
_httpClient = new HttpClient();

// media-type accept header only needs to be assigned once
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

// initialize request
_httpClient.BaseAddress = new Uri("https://example.com/");

var headersDictionary = new Dictionary<string, string>()
                        {
                            {"key1", "value1"},
                            {"key2", "value2"}
                        };

// create instance of HttpRequestMessage
var httpRequestMessageObject = new HttpRequestMessage(HttpMethod.Get, "relativePath");

var body = "content";

httpRequestMessageObject.Content = new StringContent(body, Encoding.UTF8, "application/json");

// check keys are not duplicated
foreach (var header in headersDictionary)
{
    if (!httpRequestMessageObject.Headers.Contains(header.Key))
    {
        httpRequestMessageObject.Headers.Add(header.Key, header.Value);
    }
}

// test value exists
var headerValue = string.Empty;

if (httpRequestMessageObject.Headers.Contains("key"))
{
    headerValue = httpRequestMessageObject.Headers.GetValues("key").First();
}

// invoke the request using SendAsync
var response = await _httpClient.SendAsync(httpRequestMessageObject);

var returnValue = string.Empty;

if (response.IsSuccessStatusCode)
{
    returnValue = await response.Content.ReadAsStringAsync();
}

Reference

This is demonstated further in the following Stack Overflow answers:

  1. https://stackoverflow.com/a/37929393/1165173
  2. https://stackoverflow.com/a/10679340/1165173

Upvotes: 2

user2350749
user2350749

Reputation: 61

I had the same issue with the httpClient Default request headers. See below for example using HttpRequestMessage.headers instead.

 _httpClient.DefaultRequestHeaders.Clear();
 _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/xml"));

        XDocument requestXml = JsonConvert.DeserializeXNode(message.ToString());

        HttpRequestMessage webRequest = new HttpRequestMessage()
        {
            Content = new StringContent(requestXml.Document.ToString().Replace("\r\n", string.Empty), Encoding.UTF8, "text/xml"),
            Method = HttpMethod.Post,
            RequestUri = new Uri(uri),
        };
        webRequest.Headers.Add("CorrelationId", correlationId);
        webRequest.Headers.Add("SOAPAction", endpointSOAPAction);

I was previously using the Default Request Headers for my correlationid and soap action.

Upvotes: 6

Gerald Hughes
Gerald Hughes

Reputation: 6159

The error I was getting: An item with the same key has already been added. Key: x

Example code for mahesh_ing answer:

var request = new HttpRequestMessage
{
    Method = this.method,
    RequestUri = new Uri(this.requestUri),
};

request.Headers.Add("Key", "Value");

var client = new System.Net.Http.HttpClient
{
    Timeout = this.timeout
};

return await client.SendAsync(request);

Upvotes: 7

mahesh_ing
mahesh_ing

Reputation: 429

I added header to actual (current) request using HttpRequestMessage and replaced a call with SendAsync instead of GetAsync and it resolved my issue. Thanks @levent.

Upvotes: 5

Related Questions