188

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?

2
  • what happens if you're using request.Headers.Get("MyCustomID"); ? Commented Feb 19, 2013 at 21:13
  • 3
    There is no Get' on the HttpRequestHeaders` type. The message: "Cannot resolve symbol 'Get'" is produced. Commented Feb 19, 2013 at 21:20

13 Answers 13

305

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.

Sign up to request clarification or add additional context in comments.

8 Comments

The null check for GetValues doesn't serve any value as it will never return null. If the header doesn't exist you will get an InvalidOperationException. You need to use TryGetHeaders if it's possible the header might not exist in the request and check for a false response OR try/catch around the GetValues call (not recommended).
@Drew request.Headers.Single(h => h.Key == "Authorization"); Much less code doing the same!
Why not just var id = request.Headers.GetValues("MyCustomID").FirstOrDefault();
@SaeedNeamati because header values are not one-to-one. You can have Some-Header: one and then Some-Header: two in the same request. Some languages silently discard "one" but that's incorrect. It's in the RFC but I'm too lazy to find it now.
Saeed's point is valid, usability is important and the most common use case here is to retrieve one value for a request header. You can still have a GetValues operation for retrieving multiple values for a request header (which people will arely use), but 99% of the time they'll want to just retrieve one value for a specific request header, and that should be a one liner.
|
54

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

Comments

17

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.

6 Comments

Func and Action are generic delegate signature constructs built into .NET 3.5 and above. I'd be happy to discuss specific questions about the method, but I'd recommend learning about those first.
@neontapir (and others) the second parameter is used to provide a default value if the key is not found. The third parameter is used to 'transform' the return value to be of the desired type which also specifies the type to be returned. Per the example, if 'X-MyGuid' is not found, parameter 2 lambda basically supplies a default guid as a string (as it would have been retrieved from Header) and the Guid.Parse third parameter will translate the found or default string value into a GUID.
@neontapir where is Request coming from in this function? (and if it's null how will a new HttpRequestMessage() have any headers? doesn't it make sense to just return the default value if Request is null?
It's been two years, but if I recall, a new HttpRequestMessage is initialized with an empty Headers collection, which isn't null. This function does end up returning the default value if request is null.
@mendel, neontapir I've tried using the above snippet and I believe the "Request" on line 2 of the method body should either be a private field in the class containing the method or be passed as a parameter (of type HttpRequestMessage) to the method
|
14

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

2 Comments

What if MyCustomID is not part of request.. it returns null exception.
@PrasadKanaparthi, TryGetValues are safe
11

One line solution (assuming that the header exists)

var id = request.Headers.GetValues("MyCustomID").FirstOrDefault();

5 Comments

What if MyCustomID is not part of request.. it returns null exception.
@PrasadKanaparthi then you should use another answer like stackoverflow.com/a/25640256/4275342. You see that there is no any null check, so, what is request is null? It is also possible. Or what if MyCustomID is an empty string or not equals to foo? It depends on context, so this answer just describes the way, and all validation and business logic you need to add by your own
Don't you agree that the code is working and can return header value?
It works fine.. if "MyCustomID" is part of request of request. Yes, all validation need to taken care
@PrasadKanaparthi, if the header doesn't exist you will get an InvalidOperationException, not null
10

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

3 Comments

Looks great, but GetFirstHeaderValueOrDefault has two parameters, so it complains about missing param when calling as the sample usage var myValue = response.GetFirstHeaderValueOrDefault<int>("MyValue"); Am I missing something?
Added the new static class, replaced Response for Request. Called from API controller, as var myValue = myNameSpace.HttpRequestMessageExtension.GetFirstHeaderValueOrDefault<int>("productID"); got There is no argument given that corresponds to the required formal parameter 'headerKey' of 'HttpRequestMessageExtension.GetFirstHeaderValueOrDefault<T>(HttpRequestMessage, string)'
@Jeb50 are you declaring using HttpResponseMessageExtensions on the file you're attempting to use this extension?
6

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

Comments

4

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.

Comments

3
request.Headers.FirstOrDefault( x => x.Key == "MyCustomID" ).Value.FirstOrDefault()

modern variant :)

1 Comment

What if MyCustomID is not part of request.. it returns null exception.
2
var headers = Request.Headers;
string token = headers.Contains("token") ? headers.GetValues("token").FirstOrDefault() ?? "" : "";

Comments

1

Another method

 string customHeader = string.Empty;
        if (Request.Headers.TryGetValue("x-date", out var xdateValue))
        {
            customHeader = xdateValue;
        };

Comments

0

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.

Comments

0
var token = string.Empty;
if (Request.Headers.TryGetValue("MyKey",  out headerValues))
{
    token = headerValues.FirstOrDefault();
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.