Add Custom Header in Httpwebrequest

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();
}
}
}

Sample Image

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(""));
}
}
}

GET with ordered headers
POST with ordered headers

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;
}
}
}

Sample Image

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



Leave a reply



Submit