OutputCache属性在.net 7 API端点上不起作用



我有一个API REST .NET 7和属性[OutputCache]没有缓存,即使在没有授权的端点

我添加了这个:

services.AddOutputCache(options => 
{    
options.AddBasePolicy(builder => builder.Cache()); //removing this line doesn't work either            
});

然后在Configure()中:

app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseOutputCache(); //putting it higher doesn't work either

我的端点是这样的:

[AllowAnonymous] 
[OutputCache(Duration = 99999)] 
[HttpGet] 
public async Task<IActionResult> GetAsync([FromQuery] Dto dto) => ReturnDataFromDataBase();

但是行不通。

我按照文档https://learn.microsoft.com/en-us/aspnet/core/performance/caching/output?view=aspnetcore-7.0

编辑:我补充了更多的信息,这个项目是一个。net 5,最近更新到。net 7 ([OutputCache]是在。net 7中引入的,这就是我所理解的)。它不起作用,因为每次我(用Postman)向这个端点发出请求时,它都会进入ReturnDataFromDataBase方法(我放置了一个断点)。我不能分享我的项目,因为它不是我的,但这是配置启动方法(随时纠正我):

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider)
{
//app.UseCors(builder => builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
app.UseCors();
app.UseAuthentication();
app.UseExceptionHandler("/Error");
app.UseIpRateLimiting();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
var pathBase = Configuration["APPL_PATH"];
if (pathBase == "/")
pathBase = "";
if (!string.IsNullOrEmpty(pathBase))
{
app.UsePathBase(pathBase);
}            
ServiceProviderResolver.ServiceProvider = app.ApplicationServices;
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseOutputCache();
}

编辑2:如果我将app.UseOutputCache()移动到Configure()中的第一个位置,它可以工作,但文档说它必须放在UseCors()和UseRouting()之后,在这种情况下它不起作用。

编辑3(未验证端点的解决方案):问题是app.UseMvc(),出于某种原因,所有控制器都继承自控制器(mvc),而不是从ControllerBase,我改变了它,然后我可以删除app.UseMvc(),使其工作。我还更改了顺序,如下所示:
Public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider)
{
app.UseIpRateLimiting();
var pathBase = Configuration["APPL_PATH"];
if (pathBase == "/")
pathBase = "";
if (!string.IsNullOrEmpty(pathBase))
{
app.UsePathBase(pathBase);
}
ServiceProviderResolver.ServiceProvider = app.ApplicationServices;
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.UseOutputCache();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}

下一个问题是它在需要身份验证(jwt令牌)的端点中不起作用。

默认OutputCache策略不缓存任何具有授权端点的方法。如果你想缓存授权api,你应该自定义策略来指示你想要缓存的内容。

输出缓存策略示例
public class OutputCacheWithAuthPolicy : IOutputCachePolicy
{
public static readonly OutputCacheWithAuthPolicy Instance = new();
private OutputCacheWithAuthPolicy() { }
ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
{
var attemptOutputCaching = AttemptOutputCaching(context);
context.EnableOutputCaching = true;
context.AllowCacheLookup = attemptOutputCaching;
context.AllowCacheStorage = attemptOutputCaching;
context.AllowLocking = true;
// Vary by any query by default
context.CacheVaryByRules.QueryKeys = "*";
return ValueTask.CompletedTask;
}
private static bool AttemptOutputCaching(OutputCacheContext context)
{
// Check if the current request fulfills the requirements to be cached
var request = context.HttpContext.Request;
// Verify the method, we only cache get and head verb
if (!HttpMethods.IsGet(request.Method) && !HttpMethods.IsHead(request.Method))
{
return false;
}
// we comment out below code to cache authorization response.
// Verify existence of authorization headers
//if (!StringValues.IsNullOrEmpty(request.Headers.Authorization) || request.HttpContext.User?.Identity?.IsAuthenticated == true)
//{
//    return false;
//}
return true;
}
public ValueTask ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellation) => ValueTask.CompletedTask;
public ValueTask ServeResponseAsync(OutputCacheContext context, CancellationToken cancellation) => ValueTask.CompletedTask;
}

,然后注册你的策略:

builder.Services.AddOutputCache(options =>
{
options.AddBasePolicy(builder => builder.Cache());
options.AddPolicy("OutputCacheWithAuthPolicy", OutputCacheWithAuthPolicy.Instance); 
});

那么当你缓存输出时,你应该用这个策略标记它:

[Authorize] // the end point is authorized
[OutputCache(Duration = 600, PolicyName = "OutputCacheWithAuthPolicy")] // incicate policy name
[HttpGet("GetWeatherForecastWithAuth/{id}/{second}")]
public IEnumerable<WeatherForecast> GetWithAuth([FromRoute] string id, [FromRoute] string second)

. NET核心中间件按注册顺序处理请求(见更多),因此将app.UseOutputCache();作为处理管道的最后一个元素没有意义,您应该将其移动到您想要缓存的输出的中间件之前(因此app.UseOutputCache();应该至少在app.UseEndpoints(...)之前调用):

app.UseOutputCache();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});

最新更新