WareTec
Technical Blog

Azure Functions (v3): Response Caching

Azure

This article describes how to add response caching to an Azure Functions v3 App. Response […]

This article describes how to add response caching to an Azure Functions v3 App. Response Caching can reduce the number of requests to your Functions significantly which helps you save costs and boosts performance.

In any asp.net core web app we could just add the response cache middleware, use the ResponseCache attribute and we are done. Unfortunately, the current functions runtime does not support running any middleware out of the box, so I decided to mimic the basic behavior of the ResponseCache-Attribute using my own attribute.

Be aware that my implementation has the following consequences:

  • You cannot use model binding (which is very limited anyway)
  • VaryBy is not supported
  • Cache Profiles are not supported (but could be implemented)
  • The attribute uses FunctionInvocationFilterAttribute which is currently in preview

Required NuGet Packages



Azure Functions Response Caching: The Code

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Net.Http.Headers;

namespace tebest.Func


        public override async Task OnExecutedAsync(
            FunctionExecutedContext executedContext,
            CancellationToken cancellationToken)
        {
            if (!(executedContext.Arguments.First().Value is HttpRequest request))
                throw new ApplicationException(
                    "HttpRequest is null. ModelBinding is not supported, " +
                    "please use HttpRequest as input parameter and deserialize " +
                    "using helper functions.");




            switch (cacheLocation)
            {
                case ResponseCacheLocation.Any:
                    headers.CacheControl = new CacheControlHeaderValue()
                    {
                        MaxAge = TimeSpan.FromSeconds(_duration),
                        NoStore = false,
                        Public = true
                    };
                    break;
                case ResponseCacheLocation.Client:
                    headers.CacheControl = new CacheControlHeaderValue()
                    {
                        MaxAge = TimeSpan.FromSeconds(_duration),
                        NoStore = false,
                        Public = true
                    };
                    break;
                case ResponseCacheLocation.None:
                    headers.CacheControl = new CacheControlHeaderValue()
                    {
                        MaxAge = TimeSpan.Zero,
                        NoStore = true
                    };
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            await base.OnExecutedAsync(executedContext, cancellationToken);
        }
    }
}

Usage

[FunctionName(nameof(GetCars))]
[FunctionResponseCache(60 * 60, ResponseCacheLocation.Any)]
[ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)]
public async Task GetCars(
        [HttpTrigger(AuthorizationLevel.Function, "get", Route = "v1/cars")]
    HttpRequest req,
    ILogger log)
{
    return await _carService.GetCarList(ps);
}

Notes

In case of an exception, the result will not be cached, despite what is set in the attribute. You should probably fine-tune this to check for appropriate HTTP response status codes, as you may not just throw exceptions in every scenario.

This post was initially published on tebest.net

Related posts

Zurück zum Blog