In the past, the basic way to write cache to API interface was to write code as follows:
// redis key var bookRedisKey = ConstRedisKey.RecommendationBooks.CopyOne(bookId); // Get cached data var cacheBookIds = _redisService.ReadCache<List<string>>(bookRedisKey); if (cacheBookIds != null) { // return } else { // Perform additional logic to get data and write to the cache }
And then I scattered the code everywhere.
One day, it suddenly occurred to me that the basic cache time here is almost the same, and it's all for the Web API,
Supporting caching directly in the API layer is not the end of the matter.
So, what do we do here.
In the. NET Core Web API, there are two ideas: Middleware or ActionFilter
Students who don't know can read the following documents:
Chapter IV MVC (4.3) filter of ASP.NET Core Chinese document
ASP.NET Core Chinese document Chapter 3 principle (2) middleware
Based on the fact that I only support caching for some interfaces, I can use ActionFilter directly
Didn't say, directly on the code
using System; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Newtonsoft.Json.Linq; namespace XXXAPI.Filters { public class DefaultCacheFilterAttribute : ActionFilterAttribute { // This time is used to rewrite the subclass and realize the cache at different time levels protected TimeSpan _expireTime; // redis read and write classes, not read private readonly RedisService _redisService; public DefaultCacheFilterAttribute(RedisService redisService) { _redisService = redisService; } public override void OnActionExecuting(ActionExecutingContext context) { if (context.HttpContext.Request.Query.ContainsKey("refresh")) { return; } KeyConfig redisKey = GetRequestRedisKey(context.HttpContext); var redisCache = _redisService.ReadCache<JToken>(redisKey); if (redisCache != null) { context.Result = new ObjectResult(redisCache); } return; } public override void OnActionExecuted(ActionExecutedContext context) { KeyConfig redisKey = GetRequestRedisKey(context.HttpContext); var objResult = (ObjectResult)context.Result; if (objResult == null) { return; } var jToken = JToken.FromObject(objResult.Value); _redisService.WriteCache(redisKey, jToken); } private KeyConfig GetRequestRedisKey(HttpContext httpContext) { var requestPath = httpContext.Request.Path.Value; if (!string.IsNullOrEmpty(httpContext.Request.QueryString.Value)) { requestPath = requestPath + httpContext.Request.QueryString.Value; } if (httpContext.Request.Query.ContainsKey("refresh")) { if (httpContext.Request.Query.Count == 1) { requestPath = requestPath.Replace("?refresh=true", ""); } else { requestPath = requestPath.Replace("refresh=true", ""); } } // Here is a redis key class var redisKey = ConstRedisKey.HTTPRequest.CopyOne(requestPath); if (_expireTime != default(TimeSpan)) { redisKey.ExpireTime = _expireTime; } return redisKey; } } public static class ConstRedisKey { public readonly static KeyConfig HTTPRequest = new KeyConfig() { Key = "lemon_req_", ExpireTime = new TimeSpan(TimeSpan.TicksPerMinute * 30), DBName = 5 }; } public class KeyConfig { public string Key { get; set; } public TimeSpan ExpireTime { get; set; } public int DBName { get; set; } public KeyConfig CopyOne(string state) { var one = new KeyConfig(); one.DBName = this.DBName; one.Key = !string.IsNullOrEmpty(this.Key) ? this.Key + state : state; one.ExpireTime = this.ExpireTime; return one; } } }
Then the Action method of Controller can be annotated directly
Such as:
[HttpGet("v1/xxx/latest")] [ServiceFilter(typeof(DefaultCacheFilterAttribute))] public IActionResult GetLatestList([FromQuery] int page = 0, [FromQuery]int pageSize = 30) { return Ok(new { data = _service.LoadLatest(page, pageSize), code = 0 }); }
Done...
Oh, remember to inject DefaultCacheFilterAttribute in Startup.cs
I don't need to write about this injection
The drawback is that we don't know how to support custom cache time directly on annotations,
Make do with it first
End, bye