Add custom header to all responses in Web API

C#asp.netasp.net Web-Api

C# Problem Overview


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

C# Solutions


Solution 1 - C#

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.

Solution 2 - C#

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.

Solution 3 - C#

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

Solution 4 - C#

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

Solution 5 - C#

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

Solution 6 - C#

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

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

Solution 7 - C#

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.

Solution 8 - C#

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... 

}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionModikaView Question on Stackoverflow
Solution 1 - C#Tomasz JaskuλaView Answer on Stackoverflow
Solution 2 - C#Warren RumakView Answer on Stackoverflow
Solution 3 - C#julianView Answer on Stackoverflow
Solution 4 - C#Hunter-OrionnoirView Answer on Stackoverflow
Solution 5 - C#pearldiverView Answer on Stackoverflow
Solution 6 - C#Faisal GhaffarView Answer on Stackoverflow
Solution 7 - C#NebulaView Answer on Stackoverflow
Solution 8 - C#Guillermo PerezView Answer on Stackoverflow