How to extract custom header value in Web API message handler?
C#asp.net Web-ApiHttprequestC# Problem Overview
I currently have a message handler in my Web API service that overrides 'SendAsync' as follows:
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
//implementation
}
Within this code I need to inspect a custom added request header value named MyCustomID
. The problem is when I do the following:
if (request.Headers.Contains("MyCustomID")) //OK
var id = request.Headers["MyCustomID"]; //build error - not OK
...I get the following error message:
> Cannot apply indexing with [] to an expression of type > 'System.Net.Http.Headers.HttpRequestHeaders'
How can I access a single custom request header via the HttpRequestMessage
(MSDN Documentation) instance passed into this overridden method?
C# Solutions
Solution 1 - C#
Try something like this:
IEnumerable<string> headerValues = request.Headers.GetValues("MyCustomID");
var id = headerValues.FirstOrDefault();
There's also a TryGetValues method on Headers you can use if you're not always guaranteed to have access to the header.
Solution 2 - C#
The line below throws exception
if the key does not exists.
IEnumerable<string> headerValues = request.Headers.GetValues("MyCustomID");
Safe solution using TryGetValues:
Include System.Linq;
IEnumerable<string> headerValues;
var userId = string.Empty;
if (request.Headers.TryGetValues("MyCustomID", out headerValues))
{
userId = headerValues.FirstOrDefault();
}
Solution 3 - C#
To expand on Youssef's answer, I wrote this method based Drew's concerns about the header not existing, because I ran into this situation during unit testing.
private T GetFirstHeaderValueOrDefault<T>(string headerKey,
Func<HttpRequestMessage, string> defaultValue,
Func<string,T> valueTransform)
{
IEnumerable<string> headerValues;
HttpRequestMessage message = Request ?? new HttpRequestMessage();
if (!message.Headers.TryGetValues(headerKey, out headerValues))
return valueTransform(defaultValue(message));
string firstHeaderValue = headerValues.FirstOrDefault() ?? defaultValue(message);
return valueTransform(firstHeaderValue);
}
Here's an example usage:
GetFirstHeaderValueOrDefault("X-MyGuid", h => Guid.NewGuid().ToString(), Guid.Parse);
Also have a look at @doguhan-uluca 's answer for a more general solution.
Solution 4 - C#
Create a new method - 'Returns an individual HTTP Header value' and call this method with key value everytime when you need to access multiple key Values from HttpRequestMessage.
public static string GetHeader(this HttpRequestMessage request, string key)
{
IEnumerable<string> keys = null;
if (!request.Headers.TryGetValues(key, out keys))
return null;
return keys.First();
}
Solution 5 - C#
To further expand on @neontapir's solution, here's a more generic solution that can apply to HttpRequestMessage or HttpResponseMessage equally and doesn't require hand coded expressions or functions.
using System.Net.Http;
using System.Collections.Generic;
using System.Linq;
public static class HttpResponseMessageExtensions
{
public static T GetFirstHeaderValueOrDefault<T>(
this HttpResponseMessage response,
string headerKey)
{
var toReturn = default(T);
IEnumerable<string> headerValues;
if (response.Content.Headers.TryGetValues(headerKey, out headerValues))
{
var valueString = headerValues.FirstOrDefault();
if (valueString != null)
{
return (T)Convert.ChangeType(valueString, typeof(T));
}
}
return toReturn;
}
}
Sample usage:
var myValue = response.GetFirstHeaderValueOrDefault<int>("MyValue");
Solution 6 - C#
For ASP.NET you can get the header directly from parameter in controller method using this simple library/package. It provides a [FromHeader]
attribute just like you have in ASP.NET Core :). For example:
...
using RazHeaderAttribute.Attributes;
[Route("api/{controller}")]
public class RandomController : ApiController
{
...
// GET api/random
[HttpGet]
public IEnumerable<string> Get([FromHeader("pages")] int page, [FromHeader] string rows)
{
// Print in the debug window to be sure our bound stuff are passed :)
Debug.WriteLine($"Rows {rows}, Page {page}");
...
}
}
Solution 7 - C#
One line solution (assuming that the header exists)
var id = request.Headers.GetValues("MyCustomID").FirstOrDefault();
Solution 8 - C#
For ASP.Net Core there is an easy solution if want to use the param directly in the controller method: Use the [FromHeader] annotation.
public JsonResult SendAsync([FromHeader] string myParam)
{
if(myParam == null) //Param not set in request header
{
return null;
}
return doSomething();
}
Additional Info: In my case the "myParam" had to be a string, int was always 0.
Solution 9 - C#
request.Headers.FirstOrDefault( x => x.Key == "MyCustomID" ).Value.FirstOrDefault()
modern variant :)
Solution 10 - C#
Another method
string customHeader = string.Empty;
if (Request.Headers.TryGetValue("x-date", out var xdateValue))
{
customHeader = xdateValue;
};
Solution 11 - C#
This may sound obvious, but make sure the Controller where you are reading the headers in, is the first Controller where the request goes through.
I had two WebAPI projects communicating with each other. The first one was a proxy, the second contained the logic. Silly me, I tried reading the custom headers in the second Controller, instead of the first one.
Solution 12 - C#
var token = string.Empty;
if (Request.Headers.TryGetValue("MyKey", out headerValues))
{
token = headerValues.FirstOrDefault();
}