如何在Web API消息处理程序中提取自定义标头值?


150

我目前在我的Web API服务中有一个消息处理程序,它可以覆盖“ SendAsync”,如下所示:

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
  //implementation
}

在此代码中,我需要检查一个名为的自定义添加的请求标头值MyCustomID。问题是当我执行以下操作时:

if (request.Headers.Contains("MyCustomID"))  //OK
    var id = request.Headers["MyCustomID"];  //build error - not OK

...我收到以下错误消息:

无法将[]的索引应用于类型为'System.Net.Http.Headers.HttpRequestHeaders'的表达式

如何通过传递给此重写方法的(MSDN文档)实例访问单个自定义请求标头?HttpRequestMessage


如果使用该request.Headers.Get("MyCustomID");怎么办?
udidu

2
没有Get' on the HttpRequestHeaders`类型。出现消息:“无法解析符号'Get'”。
atconway

Answers:


252

尝试这样的事情:

IEnumerable<string> headerValues = request.Headers.GetValues("MyCustomID");
var id = headerValues.FirstOrDefault();

如果不能始终保证可以访问标头,则可以在标头上使用TryGetValues方法。


26
GetValues的null检查不会提供任何值,因为它永远不会返回null。如果标题不存在,您将收到InvalidOperationException。如果请求的标头可能不存在,则需要使用TryGetHeaders,然后检查错误的响应,或者尝试对GetValues调用进行尝试/捕获(不推荐)。
德鲁·马什

4
@Drew request.Headers.Single(h => h.Key ==“授权”); 更少的代码做同样的事情!
伊丽莎白2014年

17
为什么不干脆var id = request.Headers.GetValues("MyCustomID").FirstOrDefault();
Gaui

3
@SaeedNeamati,因为标头值不是一对一的。您可以在相同的请求中包含Some-Header: one然后Some-Header: two。一些语言默默地丢弃“一个”,但这是不正确的。它在RFC中,但是我现在懒得找到它。
Cory Mawhorter,2016年

1
Saeed的观点是有效的,可用性很重要,这里最常见的用例是为请求标头检索一个值。您仍然可以使用GetValues操作来检索请求标头的多个值(人们通常会使用该值),但是在99%的时间里,他们只想检索特定请求标头的一个值,并且应该是一个衬垫。
贾斯汀

39

throws exception如果密钥不存在,则下面的行。

IEnumerable<string> headerValues = request.Headers.GetValues("MyCustomID");

解决:

包括System.Linq;

IEnumerable<string> headerValues;
var userId = string.Empty;

     if (request.Headers.TryGetValues("MyCustomID", out headerValues))
     {
         userId = headerValues.FirstOrDefault();
     }           

17

为了扩展Youssef的答案,我基于Drew对报头不存在的担忧而编写了此方法,因为我在单元测试期间遇到了这种情况。

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

这是一个示例用法:

GetFirstHeaderValueOrDefault("X-MyGuid", h => Guid.NewGuid().ToString(), Guid.Parse);

也可以查看@ doguhan-uluca的答案以获得更通用的解决方案。


1
Func并且Action是内置于.NET 3.5及以上泛型委托签名构造。我很乐意讨论有关该方法的具体问题,但我建议您先学习一下这些方法。
neontapir

1
@neontapir(和其他)(如果找不到该密钥),第二个参数用于提供默认值。第三个参数用于将返回值“转换”为所需类型,该值还指定要返回的类型。在此示例中,如果未找到“ X-MyGuid”,则参数2 lambda基本上将默认guid作为字符串提供(因为它将从Header中检索到),而Guid.Parse第三个参数将转换找到的或默认字符串值进入GUID。
Mikee

@neontapir此功能来自哪里?(如果为空,那么新的HttpRequestMessage()将如何具有任何标头?如果Request为空,仅返回默认值是否有意义?
mendel 2015年

已经两年了,但是如果我记得,一个新HttpRequestMessage的函数会使用一个空的Headers集合进行初始化,而这个集合并不为空。如果request为null,此函数最终会返回默认值。
neontapir 2015年

@mendel,neontapir我已经尝试使用上面的代码段,并且我相信方法主体第2行上的“请求”应该是包含该方法的类中的私有字段,或者作为参数(类型为HttpRequestMessage)传递给方法
Sudhanshu Mishra

12

创建一个新方法-“ 返回一个单独的HTTP标头值 ”,并在每次需要从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();
        }

如果MyCustomID不是请求的一部分,该怎么办。它返回null异常。
Prasad Kanaparthi

10

为了进一步扩展@neontapir的解决方案,这是一个更通用的解决方案,可以平等地应用于HttpRequestMessage或HttpResponseMessage,并且不需要手工编码的表达式或函数。

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

用法示例:

var myValue = response.GetFirstHeaderValueOrDefault<int>("MyValue");

看起来不错,但是GetFirstHeaderValueOrDefault有两个参数,因此在调用示例用法时会抱怨缺少参数。var myValue = response.GetFirstHeaderValueOrDefault<int>("MyValue");我是否缺少某些内容?
Jeb50 '17

添加了新的静态类,替换了“请求响应”。从API控制器调用,直到var myValue = myNameSpace.HttpRequestMessageExtension.GetFirstHeaderValueOrDefault<int>("productID");获得没有给出与“ HttpRequestMessageExtension.GetFirstHeaderValueOrDefault <T>(HttpRequestMessage,string)”的所需形式参数“ headerKey”相对应的参数
Jeb50,2015年

您是否using HttpResponseMessageExtensions在@ Jeb50上声明要使用此扩展名的文件?
Doguhan Uluca '17

4

对于ASP.Net Core,如果想直接在控制器方法中使用参数,则有一个简单的解决方案:使用[FromHeader]批注。

        public JsonResult SendAsync([FromHeader] string myParam)
        {
        if(myParam == null)  //Param not set in request header
        {
           return null;
        }
        return doSomething();
    }   

附加信息:在我的情况下,“ myParam”必须为字符串,int始终为0。


4

对于ASP.NET,您可以使用此简单的库/包直接从控制器方法中的参数获取标头。它提供的[FromHeader]属性就像在ASP.NET Core中一样:)。例如:

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

4

一线解决方案

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

如果MyCustomID不是请求的一部分,该怎么办。它返回null异常。
16:02 Prasad Kanaparthi

1
@PrasadKanaparthi,那么您应该使用另一个答案,例如stackoverflow.com/a/25640256/4275342。您会看到没有任何null检查,那么什么requestnull?这也是可能的。还是MyCustomID一个空字符串等于foo?这取决于上下文,因此,此答案仅描述您需要自己添加的方式以及所有验证和业务逻辑
Roman Marusyk

您不同意代码是否可以正常工作并且可以返回标头值吗?
Roman Marusyk

1
如果“ MyCustomID”是请求的一部分,则可以正常工作。是的,所有验证都需要注意
Prasad Kanaparthi

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.