Add custom header in HttpWebRequest
You use the Headers
property with a string index:
request.Headers["X-My-Custom-Header"] = "the-value";
According to MSDN, this has been available since:
- Universal Windows Platform 4.5
- .NET Framework 1.1
- Portable Class Library
- Silverlight 2.0
- Windows Phone Silverlight 7.0
- Windows Phone 8.1
https://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.headers(v=vs.110).aspx
Add Custom Headers using HttpWebRequest
IMHO it is considered as malformed header data.
You actually want to send those name value pairs as the request content (this is the way POST works) and not as headers.
The second way is true.
Custom headers in WebRequest NET Core 3.1
Let's take a little tour at the related source code. In System.txt there is a row:
net_WebHeaderInvalidHeaderChars=Specified value has invalid HTTP Header characters.
That means we should look for this net_WebHeaderInvalidHeaderChars
key in the source code of WebHeaderCollection:
//
// CheckBadChars - throws on invalid chars to be not found in header name/value
//
internal static string CheckBadChars(string name, bool isHeaderValue) {
...
if (isHeaderValue) {
...
}
else {
// NAME check
//First, check for absence of separators and spaces
if (name.IndexOfAny(ValidationHelper.InvalidParamChars) != -1) {
throw new ArgumentException(SR.GetString(SR.net_WebHeaderInvalidHeaderChars), "name");
}
...
}
return name;
}
This means that the error is thrown if the provided name
contains some invalid characters.
The InvalidParamChars
are defined inside the Internal
class like this:
internal static readonly char[] InvalidParamChars =
new char[]{
'(',
')',
'<',
'>',
'@',
',',
';',
':',
'\\',
'"',
'\'',
'/',
'[',
']',
'?',
'=',
'{',
'}',
' ',
'\t',
'\r',
'\n'};
So, all you have to do is to make sure that the request header name does not contain any of the not allowed characters.
How to Add Modify Headers Value with HttpWebRequest?
This method will return all headers attached with the request :
HttpWebRequest.Headers
Using .NET HttpWebRequest, how do I add a Range header with a single number?
Since you have non-standard header, .net won't allow you to add this header.
You can use the following hack:
MethodInfo method = typeof(WebHeaderCollection).GetMethod
("AddWithoutValidate", BindingFlags.Instance | BindingFlags.NonPublic);
HttpWebRequest request = (HttpWebRequest) WebRequest.Create(<your url>);
string key = "Range";
string val = $"dli-depth={depth}";
method.Invoke (request.Headers, new object[] { key, val });
Strict ordering of HTTP headers in HttpWebrequest
.Net Core
If you set the headers yourself, you can specify the order. When the common headers are added it will find the existing headers instead of appending them:
using System.Net;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
var request = WebRequest.Create("http://www.google.com");
request.Headers.Add("Host", "www.google.com");
// this will be set within GetResponse.
request.Headers.Add("Connection", "");
request.Headers.Add("Accept", "*/*");
request.Headers.Add("User-Agent", "Mozilla/5.0 etc");
request.GetResponse();
}
}
}
Here is an example with HttpClient
:
using System.Net.Http;
using System.Threading.Tasks;
namespace ConsoleApp3
{
class Program
{
static async Task Main(string[] args)
{
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Host", "www.google.com");
client.DefaultRequestHeaders.Add("Connection", "keep-alive");
client.DefaultRequestHeaders.Add("Accept", "*/*");
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 etc");
await client.GetAsync("http://www.google.com");
await client.PostAsync("http://www.google.com", new StringContent(""));
}
}
}
Edit
The above code did not work on .Net Framework only .Net Core
.Net Framework
On .Net Framework the headers are reserved so they cannot be set like this, see Cannot set some HTTP headers when using System.Net.WebRequest.
One work around is to use reflection to modify the behavior of the framework class, but be warned this could break if the libraries are updated so it's not recommended!.
Essentially, HttpWebRequest
calls ToString
on WebHeaderCollection
to serialize.
See https://referencesource.microsoft.com/#System/net/System/Net/HttpWebRequest.cs,5079
So a custom class can be made to override ToString
. Unfortunately reflection is needed to set the headers as WebRequest
copies the collection on assignment to Headers
, instead of taking the new reference.
WARNING, THE FOLLOWING CODE CAN BREAK IF FRAMEWORK CHANGES
If you use this, write some unit tests that verify the behavior still stays consistent after updates to .NET Framework
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
// WARNING, CODE CAN BREAK IF FRAMEWORK CHANGES
// If you use this, write some unit tests that verify the behavior still stays consistent after updates to .NET Framework
var request = (HttpWebRequest)WebRequest.Create("http://www.google.com");
var field = typeof(HttpWebRequest).GetField("_HttpRequestHeaders", BindingFlags.Instance | BindingFlags.NonPublic);
var headers = new CustomWebHeaderCollection(new Dictionary<string, string>
{
["Host"] = "www.google.com",
["Connection"] = "keep-alive",
["Accept"] = "*/*",
["User-Agent"] = "Mozilla/5.0 etc"
});
field.SetValue(request, headers);
request.GetResponse();
}
}
internal class CustomWebHeaderCollection : WebHeaderCollection
{
private readonly Dictionary<string, string> _customHeaders;
public CustomWebHeaderCollection(Dictionary<string, string> customHeaders)
{
_customHeaders = customHeaders;
}
public override string ToString()
{
// Could call base.ToString() split on Newline and sort as needed
var lines = _customHeaders
.Select(kvp => $"{kvp.Key}: {kvp.Value}")
// These two new lines are needed after the HTTP header
.Concat(new [] { string.Empty, string.Empty });
var headers = string.Join("\r\n", lines);
return headers;
}
}
}
Setting HttpWebRequest Header
You simply need to add:
request.Headers.Add("x-api-key", "the secret key");
where "the secret key"
is your API key.
Best way to set Content-Type header in HttpWebRequest?
Using reflection gives you the most compact code. But it's definitely not the fastest and probably not the most intuitive way to do it. Perhaps it makes sense to explicitly list all standard headers and populate them using a property setter:
var request = (HttpWebRequest)WebRequest.Create(Endpoint);
foreach (var item in Headers)
{
string headerName = item.Name.ToLower();
switch (headerName)
{
case "contenttype": request.ContentType = item.Value; break;
case "accept": request.Accept = item.Value; break;
case "useragent": request.UserAgent = item.Value; break;
// ... other standard headers
default:
// set custom header as usual
request.Headers.Add(item.Name, item.Value);
break;
}
}
Related Topics
ASP.NET Core Jwt Mapping Role Claims to Claimsidentity
Control Another Application Using C#
Differences Between Expandoobject, Dynamicobject and Dynamic
Linq to SQL Where Clause Optional Criteria
How to Throttle Requests in a Web API
Log4Net, How to Add a Custom Field to My Logging
How to Debug a Single Thread in Visual Studio
Return Content with Ihttpactionresult for Non-Ok Response
How to Get the Correct Ip from Http_X_Forwarded_For If It Contains Multiple Ip Addresses
Mstest Cannot Find the Assembly
Merge Multiple Word Documents into One Open Xml
Iterating Through the Alphabet - C# A-Caz
Need a Way to Reference 2 Different Versions of the Same 3Rd Party Dll