using Cronos; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Mirea.Api.Endpoint.Common.Attributes; using Mirea.Api.Endpoint.Configuration.Model; using System; using System.Reflection; using System.Threading.Tasks; namespace Mirea.Api.Endpoint.Middleware; public class CacheMaxAgeMiddleware(RequestDelegate next, IServiceProvider serviceProvider) { public async Task InvokeAsync(HttpContext context) { if (!context.Response.StatusCode.ToString().StartsWith('2')) { await next(context); return; } var endpoint = context.GetEndpoint(); var actionDescriptor = endpoint?.Metadata.GetMetadata(); if (actionDescriptor == null) { await next(context); return; } var controllerType = actionDescriptor.ControllerTypeInfo; var methodInfo = actionDescriptor.MethodInfo; var maxAgeAttribute = methodInfo.GetCustomAttribute() ?? controllerType.GetCustomAttribute(); if (maxAgeAttribute == null) { await next(context); return; } switch (maxAgeAttribute.MaxAge) { case < 0: { DateTime? nextDate; var now = DateTime.UtcNow; using (var scope = serviceProvider.CreateScope()) { var updateCronString = scope.ServiceProvider.GetRequiredService>().Value.ScheduleSettings?.CronUpdateSchedule; if (string.IsNullOrEmpty(updateCronString) || !CronExpression.TryParse(updateCronString, CronFormat.Standard, out var updateCron)) { await next(context); return; } nextDate = updateCron.GetNextOccurrence(now); } if (!nextDate.HasValue) { await next(context); return; } context.Response.Headers.CacheControl = "max-age=" + (int)(nextDate.Value - now).TotalSeconds; break; } case > 0: context.Response.Headers.CacheControl = "max-age=" + maxAgeAttribute.MaxAge; break; } await next(context); } }