请求数据更改时,在.NET Core 2.1中强制重新加载ResponseCache

时间:2018-11-23 13:48:11

标签: c# .net caching .net-core

我正在使用以下属性[ResponseCache(Duration = 60)]来缓存特定的GET请求,该请求在.NET Core的后端中经常被调用。

一切正常,除了在60秒内数据库中的某些数据发生更改时不重新加载缓存之外。
我必须设置一个特定的指令来重新加载/更新缓存吗? link

我的控制器中的示例代码段:

[HttpGet]
[ResponseCache(Duration = 60)]
public ActionResult<SomeTyp[]> SendDtos()
{
    var dtos = _repository.QueryAll();

    return Ok(dtos);
}

1 个答案:

答案 0 :(得分:1)

有一个使用“ ETag”,“ If-None-Match” HTTP标头的解决方案。这个想法使用的代码可以为我们提供以下问题的答案:“动作响应是否已更改?”。 如果控制器完全拥有特定的数据寿命,则可以这样做。

创建ITagProvider:

public interface ITagProvider
{
    string GetETag(string tagKey);
    void InvalidateETag(string tagKey);
}

创建动作过滤器:

public class ETagActionFilter : IActionFilter
{
    private readonly ITagProvider _tagProvider;

    public ETagActionFilter(ITagProvider tagProvider)
    {
        _tagProvider = tagProvider ?? throw new ArgumentNullException(nameof(tagProvider));
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        if (context.Exception != null)
        {
            return;
        }
        var uri = GetActionName(context.ActionDescriptor);
        var currentEtag = _tagProvider.GetETag(uri);
        if (!string.IsNullOrEmpty(currentEtag))
        {
            context.HttpContext.Response.Headers.Add("ETag", currentEtag);
        }
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        var uri = GetActionName(context.ActionDescriptor);
        var requestedEtag = context.HttpContext.Request.Headers["If-None-Match"];
        var currentEtag = _tagProvider.GetETag(uri);
        if (requestedEtag.Contains(currentEtag))
        {
            context.HttpContext.Response.Headers.Add("ETag", currentEtag);
            context.Result = new StatusCodeResult(StatusCodes.Status304NotModified);
        }
    }

    private string GetActionName(ActionDescriptor actionDescriptor)
    {
        return $"{actionDescriptor.RouteValues["controller"]}.{actionDescriptor.RouteValues["action"]}";
    }
}

初始化Startup类中的过滤器:

public void ConfigureServices(IServiceCollection services)
{
    // code above
    services.AddMvc(options =>
        {
            options.Filters.Add(typeof(ETagActionFilter));
        });
    services.AddScoped<ETagActionFilter>();
    services.AddSingleton<ITagProvider, TagProvider>();
    // code below
}

在控制器中的某处(在您修改数据的地方)使用InvalidateETag方法:

    [HttpPost]
    public async Task<ActionResult> Post([FromBody] SomeType data)
    {
        // TODO: Modify data
        // Invalidate tag
        var tag = $"{controllerName}.{methodName}"
        _tagProvider.InvalidateETag(tag);
        return NoContent();
    }

此解决方案可能需要更改客户端。如果使用提取,则可以使用例如以下库:https://github.com/export-mike/f-etag

P.S。我没有指定ITagProvider接口的实现,您将需要编写自己的接口。 P.P.S.有关ETag和缓存的文章:https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-cachinghttps://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag