Modika
Modika

Reputation: 6282

Add custom header to all responses in Web API

Simple question, and I am sure it has a simple answer but I can't find it.

I am using WebAPI and I would like to send back a custom header to all responses (server date/time requested by a dev for syncing purposes).

I am currently struggling to find a clear example of how, in one place (via the global.asax or another central location) I can get a custom header to appear for all responses.


Answer accepted, here is my filter (pretty much the same) and the line i added to the Register function of the WebApi config.

NOTE: The DateTime stuff is NodaTime, no real reason just was interested in looking at it.

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

Config Line:

config.Filters.Add(new ServerTimeHeaderFilter());

Upvotes: 67

Views: 95781

Answers (8)

pearldiver
pearldiver

Reputation: 517

It can be done by the messagehandler easily, it will handle both ok response and exception case.

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

Add it in the config

 config.MessageHandlers.Add(new CustomHeaderHandler());

Upvotes: 3

Guillermo Perez
Guillermo Perez

Reputation: 649

I had the same problem while trying to add a new header to the whole controller, just add "services.AddHttpContextAccessor();" to startup.cs then create your controller

public class EnController : Controller{

        public EnController(IHttpContextAccessor myHttpAccessor)
        {

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

        }

       ... more methods here... 

}

Upvotes: 0

Faisal Ghaffar
Faisal Ghaffar

Reputation: 181

According to my requirement, below single line of code serves the purpose.

System.Web.HttpContext.Current.Response.Headers.Add("Key", "Value")

Upvotes: 1

julian
julian

Reputation: 368

Neither of the above two solutions worked for me. They wouldn't even compile. Here's what I did. Added:

filters.Add(new AddCustomHeaderFilter());

to RegisterGlobalFilters(GlobalFilterCollection filters) method in FiltersConfig.cs and then added

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

Upvotes: 6

Tomasz Jaskuλa
Tomasz Jaskuλa

Reputation: 16013

For that you can use a custom ActionFilter (System.Web.Http.Filters)

public class AddCustomHeaderFilter : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
       actionExecutedContext.Response.Headers.Add("customHeader", "custom value date time");
    }
}

You can then apply the filter to all your controller's actions by adding this in the configuration in Global.asax for example :

GlobalConfiguration.Configuration.Filters.Add(new AddCustomHeaderFilter());

You can also apply the filter attribute to the action that you want without the global cofiguration line.

Upvotes: 111

Nebula
Nebula

Reputation: 1057

I combined the normal and exception path in one class:

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

Nothing fancy but at least it gives me one place to control my additional headers. For now it's just static content but you could always hook it up to some sort of dictionary generator/factory.

Upvotes: 0

Warren Rumak
Warren Rumak

Reputation: 3864

Previous answers to this question don't address what to do if your controller action throws an exception. There are two basic ways to get that to work:

Add an exception filter:

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

and in your WebApi setup:

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

This approach works because WebApi's default exception handler will send the HttpResponseMessage created in a filter instead of building its own.

Replace the default exception handler:

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

and in your WebApi setup:

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

You can't use both of these together. Okay, well, you can, but the handler will never do anything because the filter already converted the exception into a response.

Super important to note that as written, this code will send all the exception details to the client. You probably don't want to do this in production, so check out all the available overloads on CreateErrorResponse() and pick which one suits your needs.

Upvotes: 8

Hunter-Orionnoir
Hunter-Orionnoir

Reputation: 2073

Julian's answer led me to have to create the filter but only using the the System.Web (v4) and System.Web.Http (v5) namespace (MVC packages were not part of this particular project this was used on.)

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

And add it to the global.asax to have it used on every controller/action

        GlobalConfiguration.Configuration.Filters.Add(new AddCustomHeaderActionFilterAttribute());

Upvotes: 6

Related Questions