Reputation: 429
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
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:
Upvotes: 2
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
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
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