如何在 Web API 消息处理程序中提取自定义标头值?
Posted
技术标签:
【中文标题】如何在 Web API 消息处理程序中提取自定义标头值?【英文标题】:How to extract custom header value in Web API message handler? 【发布时间】:2013-02-04 17:20:39 【问题描述】:我目前在我的 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'
如何通过传递给此重写方法的HttpRequestMessage
(MSDN Documentation) 实例访问单个 自定义请求标头?
【问题讨论】:
如果你使用request.Headers.Get("MyCustomID");
会发生什么?
没有Get' on the
HttpRequestHeaders` 类型。产生消息:“无法解析符号'Get'”。
【参考方案1】:
试试这样的:
IEnumerable<string> headerValues = request.Headers.GetValues("MyCustomID");
var id = headerValues.FirstOrDefault();
还有一个 TryGetValues 方法可以用于标头,如果您不能始终保证可以访问标头。
【讨论】:
GetValues 的 null 检查不提供任何值,因为它永远不会返回 null。如果标头不存在,您将收到 InvalidOperationException。如果请求中可能不存在标头,则需要使用 TryGetHeaders 并检查错误响应或尝试/捕获 GetValues 调用(不推荐)。 @Drew request.Headers.Single(h => h.Key == "授权");更少的代码做同样的事情! 为什么不只是var id = request.Headers.GetValues("MyCustomID").FirstOrDefault();
@SaeedNamati 因为标头值不是一对一的。您可以在同一请求中拥有 Some-Header: one
和 Some-Header: two
。有些语言会默默地丢弃“一”,但这是不正确的。它在 RFC 中,但我现在懒得找。
Saeed 的观点是正确的,可用性很重要,这里最常见的用例是检索请求标头的一个值。您仍然可以使用 GetValues 操作来检索请求标头的多个值(人们会使用),但 99% 的时间他们只想检索特定请求标头的一个值,而这应该是一个班轮。【参考方案2】:
如果密钥不存在,则在throws exception
下面的行。
IEnumerable<string> headerValues = request.Headers.GetValues("MyCustomID");
使用 TryGetValues 的安全解决方案:
包括 System.Linq;
IEnumerable<string> headerValues;
var userId = string.Empty;
if (request.Headers.TryGetValues("MyCustomID", out headerValues))
userId = headerValues.FirstOrDefault();
【讨论】:
【参考方案3】:为了扩展 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 的答案以获得更通用的解决方案。
【讨论】:
Func
和 Action
是内置于 .NET 3.5 及更高版本的通用委托签名结构。我很乐意讨论有关该方法的具体问题,但我建议先了解这些问题。
@neontapir(和其他人)第二个参数用于在未找到密钥时提供默认值。第三个参数用于将返回值“转换”为所需类型,该类型还指定要返回的类型。根据示例,如果未找到“X-MyGuid”,则参数 2 lambda 基本上将默认 guid 作为字符串提供(因为它会从 Header 中检索到),并且 Guid.Parse 第三个参数将转换找到的或默认的字符串值到一个 GUID。
@neontapir 这个函数的请求来自哪里? (如果它为空,新的 HttpRequestMessage() 将如何具有任何标头?如果 Request 为空,仅返回默认值是否有意义?
已经两年了,但如果我记得,一个新的HttpRequestMessage
被初始化为一个空的 Headers 集合,它不为空。如果请求为空,此函数最终会返回默认值。
@mendel,neontapir 我已经尝试使用上面的 sn-p,我相信方法主体第 2 行的“请求”应该是包含该方法的类中的私有字段,或者是作为参数(HttpRequestMessage 类型)传递给方法【参考方案4】:
创建一个新方法 - '返回一个单独的 HTTP Header 值',并在每次需要从 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 异常。 @PrasadKanaparthi,TryGetValues 是安全的【参考方案5】:为了进一步扩展 @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");
我错过了什么吗?
添加了新的静态类,替换了请求的响应。从 API 控制器调用,因为 var myValue = myNameSpace.HttpRequestMessageExtension.GetFirstHeaderValueOrDefault<int>("productID");
得到 没有给定的参数对应于 'HttpRequestMessageExtension.GetFirstHeaderValueOrDefaultusing HttpResponseMessageExtensions
?【参考方案6】:
对于 ASP.NET,您可以使用 this simple library/package 直接从控制器方法中的参数获取标头。它提供了一个 [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");
...
【讨论】:
【参考方案7】:一行解决方案(假设表头存在)
var id = request.Headers.GetValues("MyCustomID").FirstOrDefault();
【讨论】:
如果 MyCustomID 不是请求的一部分怎么办......它返回 null 异常。 @PrasadKanaparthi 那么您应该使用另一个答案,例如***.com/a/25640256/4275342。你看到没有任何空检查,那么request
是null
是什么?这也是可能的。或者如果MyCustomID
是一个空字符串或不等于foo
怎么办?这取决于上下文,所以这个答案只是描述了方式,以及您需要自己添加的所有验证和业务逻辑
您不同意代码正在运行并且可以返回标头值吗?
它工作正常..如果“MyCustomID”是请求请求的一部分。是的,所有验证都需要注意
@PrasadKanaparthi,如果标头不存在,您将收到 InvalidOperationException,而不是 null【参考方案8】:
对于 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。
【讨论】:
【参考方案9】:request.Headers.FirstOrDefault( x => x.Key == "MyCustomID" ).Value.FirstOrDefault()
现代变体:)
【讨论】:
如果 MyCustomID 不是请求的一部分怎么办......它返回 null 异常。【参考方案10】:这听起来很明显,但请确保您正在读取标头的控制器是请求通过的第一个控制器。
我有两个相互通信的 WebAPI 项目。第一个是代理,第二个包含逻辑。愚蠢的我,我尝试在第二个控制器中读取自定义标题,而不是第一个。
【讨论】:
【参考方案11】:var token = string.Empty;
if (Request.Headers.TryGetValue("MyKey", out headerValues))
token = headerValues.FirstOrDefault();
【讨论】:
【参考方案12】:另一种方法
string customHeader = string.Empty;
if (Request.Headers.TryGetValue("x-date", out var xdateValue))
customHeader = xdateValue;
;
【讨论】:
以上是关于如何在 Web API 消息处理程序中提取自定义标头值?的主要内容,如果未能解决你的问题,请参考以下文章