Add a custom response header in ApiController

68,359

Solution 1

I have entered comments, here is my complete answer.

You will need to create a custom filter and apply that to your controller .

public class CustomHeaderFilter : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
       var count = actionExecutedContext.Request.Properties["Count"];
       actionExecutedContext.Response.Content.Headers.Add("totalHeader", count);
    }
}

In your Controller

  public class AddressController : ApiController
        {
            public async Task<Address> Get()
            {
               Request.Properties["Count"] = "123";
            }
    }

Solution 2

You can explicitly add custom headers in a method like so:

[HttpGet]
[Route("home/students")]
public HttpResponseMessage GetStudents()
{
       // Get students from Database

       // Create the response
        var response = Request.CreateResponse(HttpStatusCode.OK, students);
    
        // Set headers for paging
        response.Headers.Add("X-Students-Total-Count", students.Count());
       
       return response;
}

For more information read this article: http://www.jerriepelser.com/blog/paging-in-aspnet-webapi-http-headers/

Solution 3

Simple solution is to write just this:

HttpContext.Current.Response.Headers.Add("MaxRecords", "1000");

Solution 4

What you need is:

public async Task<IHttpActionResult> Get() 
{ 
    var response = Request.CreateResponse();
    response.Headers.Add("Lorem", "ipsum");

    return base.ResponseMessage(response); 
}

I hope this answers your question.

Solution 5

Alternatively, it’s better to leverage DelegatingHandler if it is something you need to perform on every response. Because it will work on the request/response pipeline and not on the controller/action level. In my case I must add some headers with every response, so I did what I described. See code snippet below

public class Interceptor : DelegatingHandler
{
    protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var response = await base.SendAsync(request, cancellationToken);
        response.Headers.Add("Access-Control-Allow-Origin", "*");
        response.Headers.Add("Access-Control-Allow-Methods", "GET,POST,PATCH,DELETE,PUT,OPTIONS");
        response.Headers.Add("Access-Control-Allow-Headers", "Origin, Content-Type, X-Auth-Token, content-type");
        return response;
    }

}

And you would be requiring to add this handler in WebApiConfig

    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.MessageHandlers.Add(new Interceptor());
        }
    } 
Share:
68,359
Matias Cicero
Author by

Matias Cicero

Software Development Engineer @ Amazon Web Services

Updated on April 06, 2021

Comments

  • Matias Cicero
    Matias Cicero about 3 years

    Until now, I had a GET method that looked like the following:

    protected override async Task<IHttpActionResult> GetAll(QueryData query)
    {
         // ... Some operations
    
         //LINQ Expression based on the query parameters
         Expression<Func<Entity, bool>> queryExpression = BuildQueryExpression(query);
    
         //Begin to count all the entities in the repository
         Task<int> countingEntities = repo.CountAsync(queryExpression);
    
         //Reads an entity that will be the page start
         Entity start = await repo.ReadAsync(query.Start);
    
         //Reads all the entities starting from the start entity
         IEnumerable<Entity> found = await repo.BrowseAllAsync(start, queryExpression);
    
         //Truncates to page size
         found = found.Take(query.Size);
    
         //Number of entities returned in response
         int count = found.Count();
    
         //Number of total entities (without pagination)
         int total = await countingEntities;
    
         return Ok(new {
              Total = total,
              Count = count,
              Last = count > 0 ? GetEntityKey(found.Last()) : default(Key),
              Data = found.Select(e => IsResourceOwner(e) ? MapToOwnerDTO(e) : MapToDTO(e)).ToList()
         });
    }
    

    This worked like a charm and it was good. However, I was told recently to send the response metadata (that is, Total, Count and Last properties) as response custom headers instead of the response body.

    I cannot manage to access the Response from the ApiController. I thought of a filter or attribute, but how would I get the metadata values?

    I can keep all this information on the response and then have a filter that will deserialize the response before being sent to the client, and create a new one with the headers, but that seems troublesome and bad.

    Is there a way to add custom headers directly from this method on an ApiController?

  • Matias Cicero
    Matias Cicero over 8 years
    But how would I get the "values" of the headers?
  • Yousuf
    Yousuf over 8 years
    Interesting Questions. It looks like you can set property Request.Properties["Count"] = "123" in controller and use it in the filter.
  • Yousuf
    Yousuf over 8 years
    In the filter, you can access it by actionContext.Request.Properties["Count"]
  • Matias Cicero
    Matias Cicero over 8 years
    This works perfectly but is it the correct way to do this? My metadata should be a property of the response, not the request. I mean, it works as a solution, but is it conceptually right?
  • weagle08
    weagle08 about 7 years
    I'm doing this, but the headers get stripped
  • Seagull
    Seagull about 7 years
    @weagle08 Does your request go through proxies? If so you can read this: stackoverflow.com/questions/20820572/…
  • nkalfov
    nkalfov almost 7 years
    This looks like double work to me. You may add a header directly
  • Admin
    Admin over 6 years
    Worked for me but no proxy involved in our connection
  • Sten Petrov
    Sten Petrov over 6 years
    @Nikola but then you lose the strongly typed response, which the OP didn't use but is still an option with this approach. I'm working on a web api project and not using strong types is causing issues - for one we can't easily generate correct swagger. Avoid returning untyped responses if you can
  • Deepak
    Deepak over 5 years
    Thanks Darek, for code highlighting. I will make sure to do this from now onwards :)
  • Michael Erickson
    Michael Erickson over 5 years
    In my case I found this to be the best solution for returning response data in the headers, but you have to be careful where the action filter gets the data. I has to get the data for the request you are processing, I looked all over the place for data storage that is unique to the request and the only thing I could find is the "context.Request.Properties" table, which is most likely why @Yousuf used it. Keep in mine that the "context.Response" object does not exist while processing the action, so "context.Request" seems to be the only place you can store data like this.
  • Michael Erickson
    Michael Erickson over 5 years
    Regarding strongly typed response, unfortunately that is the nature of the HTTP protocol, all data is text. You could consider some XML or JSON format that includes typing to verify the transfer of data.
  • ShellNinja
    ShellNinja about 5 years
    HttpContext would not be present in Controllers that derive ApiController.
  • Paul DeVito
    Paul DeVito over 3 years
    This is indeed to right answer if we're talking about a .net framework controller that derive from system.web.http.apicontroller. I'm personally just familiar with the core syntax, so this was great for this legacy project. cheers
  • Andrew Gale
    Andrew Gale about 3 years
    This worked for me. My controller is inheriting from ApiController
  • Kit
    Kit over 2 years
    Absolute lifesaver in my legacy project.
  • Yossi Geretz
    Yossi Geretz over 2 years
    I used this in my class which derives from System.Web.Http.ApiController. (.NET Framework - 4.5.2.) Works perfectly.