From d5a755f2ba5a153d2bd039933ad80b42ed9cd701 Mon Sep 17 00:00:00 2001 From: Collin Barrett <6483057+collinbarrett@users.noreply.github.com> Date: Sat, 31 May 2025 21:11:17 -0500 Subject: [PATCH] refactor(api): cache api responses --- .../CacheMiddleware.cs | 48 +++++++++++++++++++ .../FilterLists.Directory.Api/Program.cs | 1 + 2 files changed, 49 insertions(+) create mode 100644 services/Directory/FilterLists.Directory.Api/CacheMiddleware.cs diff --git a/services/Directory/FilterLists.Directory.Api/CacheMiddleware.cs b/services/Directory/FilterLists.Directory.Api/CacheMiddleware.cs new file mode 100644 index 000000000..805cb7f7b --- /dev/null +++ b/services/Directory/FilterLists.Directory.Api/CacheMiddleware.cs @@ -0,0 +1,48 @@ +namespace FilterLists.Directory.Api; + +internal class CachingMiddleware(RequestDelegate next, CachingMiddlewareOptions options) +{ + public async Task InvokeAsync(HttpContext context) + { + await next(context); + + if (ShouldApplyCachingHeaders(context)) + { + context.Response.Headers.CacheControl = + $"public, s-maxage={options.SharedCacheMaxAgeSeconds}, max-age={options.ClientCacheMaxAgeSeconds}"; + + if (options.AddExpiresHeader) + context.Response.Headers.Expires = + DateTime.UtcNow.AddSeconds(options.SharedCacheMaxAgeSeconds).ToString("R"); + } + } + + private bool ShouldApplyCachingHeaders(HttpContext context) + { + return context.Request.Path.StartsWithSegments(options.ApiPathPrefix) && + context.Request.Method == HttpMethods.Get && + context.Response.StatusCode >= 200 && + context.Response.StatusCode < 300 && + !context.Response.Headers.ContainsKey("Cache-Control"); + } +} + +internal record CachingMiddlewareOptions +{ + public string ApiPathPrefix { get; set; } = "/api"; + public int SharedCacheMaxAgeSeconds { get; set; } = 24 * 60 * 60; // Default 1 day + public int ClientCacheMaxAgeSeconds { get; set; } = 1 * 60 * 60; // Default 1 hour + public bool AddExpiresHeader { get; set; } +} + +internal static class CachingMiddlewareExtensions +{ + public static IApplicationBuilder UseCustomApiCaching( + this IApplicationBuilder builder, + Action? configureOptions = null) + { + var options = new CachingMiddlewareOptions(); + configureOptions?.Invoke(options); + return builder.UseMiddleware(options); + } +} \ No newline at end of file diff --git a/services/Directory/FilterLists.Directory.Api/Program.cs b/services/Directory/FilterLists.Directory.Api/Program.cs index 5182d8778..098493fd8 100644 --- a/services/Directory/FilterLists.Directory.Api/Program.cs +++ b/services/Directory/FilterLists.Directory.Api/Program.cs @@ -16,6 +16,7 @@ app.UseExceptionHandler(); app.UseCors(); +app.UseCustomApiCaching(); app.MapEndpoints(); app.MapDefaultEndpoints(); app.UseSwagger(o => o.RouteTemplate = "{documentName}/openapi.json");