23

I'm working on an Azure Function App (v2), which has a number of different functions inside the same Function App.

It is going to be deployed in multiple Azure regions, and I would like to add a custom header, that indicates which region served the request.

I know that I can let each function return HttpResponseMessage which can contain custom headers. Instead of duplicating that code to each function, I would like to make it centralized in the entire Function App project. Another drawback of returning HttpResponseMessage is that I would like to use IActionResult, so I can use JsonResult or OkObjectResult and similar return types.

With an ASP.NET MVC project, I could add a middleware which added the header or add the headers using web.config. Neither seems to be possible in a Function App.

How can this be done without having to return HttpResponseMessage and adding the header inside each function's Run method?

RasmusW
  • 3,355
  • 3
  • 28
  • 46
  • 2
    Hve a look at function filter, it should fit your requirements: https://github.com/Azure/azure-webjobs-sdk/wiki/Function-Filters. – Thomas Jan 17 '19 at 09:16
  • 1
    Thank you for the pointer. Function filters are close to what I want. At least they are injected into the Function's execution flow. But it seems the feature isn't done yet (https://github.com/Azure/azure-webjobs-sdk/issues/1284) and they can't modify the function's response (https://github.com/Azure/azure-webjobs-sdk/issues/1314) – RasmusW Jan 17 '19 at 13:54

3 Answers3

42

I was able to do this by accessing the HttpResponse object via the request's HttpContext.

For example:

req.HttpContext.Response.Headers.Add("ETag", entity.ETag);
return new OkObjectResult(entity);

produces: enter image description here

bvpb
  • 1,416
  • 22
  • 31
  • I tried sublcassing `OkObjectResult` but I can't set the header from in there. Was really hoping to make it cleaner. Any other ideas? – Bill Noel Nov 12 '19 at 00:25
  • 2
    @BillNoel custom headers need to be set on the `Response` not the result. The result is only concerned with the return value, status code and content type (which I believe will affect the *Content-Type* header for the response), but to set a custom header you need to use the `HttpResponse`. – bvpb Nov 12 '19 at 02:41
  • where I can find response headers in HttpRequestMessage object? – TeoVr81 Jun 08 '21 at 14:49
3

while researching the exact same problem, I came along this blog post. Basically, you derive a custom class fomr JsonResult, OKResult and so on and manipulate the ExecuteResultAsync method and add your headers there.

public class ServiceUnavailableResult : StatusCodeResult {
    private readonly int _retryAfterHeaderValue;

    public ServiceUnavailableResult(int retryAfterHeaderValue) : base(500) {
        _retryAfterHeaderValue = retryAfterHeaderValue;
    }

    public override async Task ExecuteResultAsync(ActionContext context) {
        await base.ExecuteResultAsync(context);
        
        context.HttpContext.Response.Headers.Add("Retry-After", _retryAfterHeaderValue.ToString());
    }
}

It does not fully help you, but if you only use like two or three classes when returning content, it might.

Anton Georgiev
  • 610
  • 6
  • 14
1

It is now possible to inject middleware that can add custom headers to functions responses.

Registration

public static void Main()
{
  var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults(workerApplication =>
    {
      workerApplication.UseWhen<StampHttpHeaderMiddleware>((context) =>
      {
        // We want to use this middleware only for http trigger invocations.
        return context.FunctionDefinition.
          InputBindings.Values.First(
            a => a.Type.EndsWith("Trigger")).Type == "httpTrigger";
      });
    })
   .Build();
  host.Run();

Implementation

internal sealed class StampHttpHeaderMiddleware : IFunctionsWorkerMiddleware
{
    public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
    {
      context.GetHttpResponseData()?.Headers.Add("x-myCustomHeader", "custom header value");
    }
}

(code is adapted from the GitHub sample)

RasmusW
  • 3,355
  • 3
  • 28
  • 46