How to extract custom header value in Web API message handler?

C#asp.net Web-ApiHttprequest

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionatconwayView Question on Stackoverflow
Solution 1 - C#Youssef MoussaouiView Answer on Stackoverflow
Solution 2 - C#SharpCoderView Answer on Stackoverflow
Solution 3 - C#neontapirView Answer on Stackoverflow
Solution 4 - C#SRIView Answer on Stackoverflow
Solution 5 - C#Doguhan UlucaView Answer on Stackoverflow
Solution 6 - C#lawrenceagbaniView Answer on Stackoverflow
Solution 7 - C#Roman MarusykView Answer on Stackoverflow
Solution 8 - C#ReinerView Answer on Stackoverflow
Solution 9 - C#Konstantin SalavatovView Answer on Stackoverflow
Solution 10 - C#rajquestView Answer on Stackoverflow
Solution 11 - C#tno2007View Answer on Stackoverflow
Solution 12 - C#Saon MukherjeeView Answer on Stackoverflow