在Web API中添加自定义标头以应用于所有响应

67

这是一个简单的问题,我相信它有一个简单的答案,但我找不到它。

我正在使用WebAPI,并希望为所有响应发送自定义标头(由开发人员请求的服务器日期/时间,以进行同步)。

我目前正在努力寻找如何在一个地方(通过global.asax或另一个中心位置)获取自定义标头的清晰示例,以便出现在所有响应中。


回答已被接受,这是我的过滤器(几乎相同),以及我在WebApi配置的Register函数中添加的行。

注意:DateTime的内容是NodaTime,没有真正的原因,只是对它感兴趣。

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        actionExecutedContext.Response.Content.Headers.Add("ServerTime", Instant.FromDateTimeUtc(DateTime.Now.ToUniversalTime()).ToString());
    }

配置行:

config.Filters.Add(new ServerTimeHeaderFilter());
8个回答

111

你可以使用自定义的ActionFilter(System.Web.Http.Filters)来实现这一点。

public class AddCustomHeaderFilter : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
       actionExecutedContext.Response.Headers.Add("customHeader", "custom value date time");
    }
}
您可以在Global.asax的配置中添加以下内容,然后将该过滤器应用于您控制器的所有操作:
GlobalConfiguration.Configuration.Filters.Add(new AddCustomHeaderFilter());

你还可以将过滤器属性应用于你想要的操作,而无需全局配置行。


4
这个答案大部分正确。有时候响应内容可能是null,你应该改为使用actionExecutedContext.Response.Headers.Add - George
动作过滤器可能有效,但要小心消息处理程序可能会阻止您甚至到达那一步。因此,您可能会发现一些响应缺少标题。 - Oliver
另外,请记住,授权过滤器在操作过滤器之前运行,如果您的授权过滤器拒绝访问,则自定义操作过滤器根本不会触发。 - Mihai Caracostea
2
这些答案的问题在于它们只为HTML页面应用标题,而不是CSS页面、图像、JavaScript等。这真的是个问题吗?如果你使用ZAP进行渗透测试,它会抱怨缺少标题。如何为MVC应用程序中的所有文件添加标题? - Gullbyrd
1
OnActionExecuted只有在执行操作时才会触发。因此,如果另一个过滤器阻止了它的执行,那么您的过滤器将不会运行。如果您已经设置了授权过滤器,并且上下文未经过身份验证,而您依赖于您的操作过滤器在未经身份验证的请求上运行,则这是很常见的情况。为了解决这个问题,您应该实现OnActionExecuting,并实现它的异步和同步版本。 - Ryan Mann
所以这对我来说是有效的,现在却没有了。HttpActionExecutedContext.Response现在由于某种原因为空。 - Chris Ward

8

之前对这个问题的回答没有解决如果控制器操作引发异常应该怎么做。有两种基本方法可以解决这个问题:

添加异常过滤器:

using System.Net;
using System.Net.Http;
using System.Web.Http.Filters;

public class HeaderAdderExceptionFilter : ExceptionFilterAttribute
{
    public override void OnException(HttpActionExecutedContext context)
    {
        if (context.Response == null)
            context.Response = context.Request.CreateErrorResponse(
                HttpStatusCode.InternalServerError, context.Exception);

        context.Response.Content.Headers.Add("header", "value");
    }
}

在您的WebApi设置中:

configuration.Filters.Add(new HeaderAdderExceptionFilter());

这种方法有效是因为WebApi的默认异常处理程序将发送在过滤器中创建的HttpResponseMessage,而不是构建自己的HttpResponseMessage。

替换默认异常处理程序:

using System.Net;
using System.Net.Http;
using System.Web.Http.ExceptionHandling;
using System.Web.Http.Results;

public class HeaderAdderExceptionHandler : ExceptionHandler
{
    public override void Handle(ExceptionHandlerContext context)
    {
        HttpResponseMessage response = context.Request.CreateErrorResponse(
            HttpStatusCode.InternalServerError, context.Exception);
        response.Headers.Add("header", "value");

        context.Result = new ResponseMessageResult(response);
    }
}

在您的WebApi设置中:

configuration.Services.Replace(typeof(IExceptionHandler), new HeaderAdderExceptionHandler());

您不能同时使用这两个功能。好吧,您可以这样做,但是处理程序将永远不会执行任何操作,因为过滤器已经将异常转换为响应。

非常重要的是注意,按照现有代码,此代码将向客户端发送所有异常细节。在生产环境中,您可能不希望这样做,请查看CreateErrorResponse()上的所有可用重载,并选择适合您需要的重载。


6

Julian的回答引导我创建过滤器,但仅使用System.Web (v4)和System.Web.Http (v5)命名空间(MVC包不是此特定项目的一部分,所以没有使用它们)。

using System.Web;
using System.Web.Http.Filters;
...
public class AddCustomHeaderActionFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        base.OnActionExecuted(actionExecutedContext);
        actionExecutedContext.ActionContext.Response.Headers.Add("name", "value");
    }
}

把它添加到 global.asax 中,以便在每个控制器/操作中都能使用。
        GlobalConfiguration.Configuration.Filters.Add(new AddCustomHeaderActionFilterAttribute());

6

以上两种解决方案都不适用于我。它们甚至无法编译。这是我所做的。添加了:

filters.Add(new AddCustomHeaderFilter());

需要在FiltersConfig.cs文件的RegisterGlobalFilters(GlobalFilterCollection filters)方法中添加以下内容:

public class AddCustomHeaderFilter : ActionFilterAttribute
{
   public override void OnActionExecuted(ActionExecutedContext actionExecutedContext)
   {
       actionExecutedContext.HttpContext.Response.Headers.Add("ServerTime", DateTime.Now.ToString());
   }
}

我认为这是WebAPI 1,不确定你是否使用的是2(如果是的话),它可能具有稍微不同的绑定。 - Modika
是的,很可能就是这个解释。我正在使用WebAPI 2.0。但我认为我的版本将对现在正在寻找如何添加自定义标头的人有所帮助。 - julian
我认为当抛出异常时,这个不起作用。你如何将头部添加到所有响应中? - thebiggestlebowski

3

通过消息处理程序很容易完成,它可以处理ok响应和异常情况。

 public class CustomHeaderHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
       // add header to request if you want
        var response = await base.SendAsync(request, cancellationToken);
        response.Headers.Add("cutomKey", "cutomValue");
        return response;
    }
}

在配置文件中添加它。
 config.MessageHandlers.Add(new CustomHeaderHandler());

1
根据我的要求,下面的单行代码可以达到目的。
System.Web.HttpContext.Current.Response.Headers.Add("Key", "Value")

IEnumerable<string> vals; response.Headers.TryGetValues("Key", out vals); string value = vals?.FirstOrDefault(); - Faisal Ghaffar

0

我在尝试为整个控制器添加新标题时遇到了同样的问题,只需在startup.cs中添加“services.AddHttpContextAccessor();”,然后创建您的控制器即可。

public class EnController : Controller{

        public EnController(IHttpContextAccessor myHttpAccessor)
        {

            myHttpAccessor.HttpContext.Response.Headers.Add("Content-Language", "en-US");

        }

       ... more methods here... 

}


0

我将正常路径和异常路径合并到一个类中:

public class CustomHeaderAttribute : FilterAttribute, IActionFilter, IExceptionFilter
{
    private static string HEADER_KEY   { get { return "X-CustomHeader"; } }
    private static string HEADER_VALUE { get { return "Custom header value"; } }

    public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
    {
        return (new CustomHeaderAction() as IActionFilter).ExecuteActionFilterAsync(actionContext, cancellationToken, continuation);
    }

    public Task ExecuteExceptionFilterAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
    {
        return (new CustomHeaderException() as IExceptionFilter).ExecuteExceptionFilterAsync(actionExecutedContext, cancellationToken);
    }

    private class CustomHeaderAction: ActionFilterAttribute
    {
        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            if (actionExecutedContext.Response != null)
            { 
                actionExecutedContext.Response.Content.Headers.Add(HEADER_KEY, HEADER_VALUE);
            }
        }
    }

    private class CustomHeaderException : ExceptionFilterAttribute
    {
        public override void OnException(HttpActionExecutedContext context)
        {
            if (context.Response == null)
            {
                context.Response = context.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, context.Exception);
            }

            context.Response.Content.Headers.Add(HEADER_KEY, HEADER_VALUE);
        }
    }
}

没有什么花哨的东西,但至少它给了我一个地方来控制我的额外头部信息。目前只是静态内容,但你总可以将其连接到某种字典生成器/工厂。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接