کش خروجی API در ASP.NET Core با Redis
نویسنده: فرشاد داودی
تاریخ: ۱۳۹۹/۰۱/۲۶ ۱۳:۳۵
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
Install-Package Microsoft.Extensions.Caching.StackExchangeRedis
public interface IResponseCacheService
{
Task CacheResponseAsync(string cacheKey, object response, TimeSpan timeToLive);
Task<string> GetCachedResponseAsync(string cacheKey);
} public class ResponseCacheService : IResponseCacheService, ISingletonDependency
{
private readonly IDistributedCache _distributedCache;
public ResponseCacheService(IDistributedCache distributedCache)
{
_distributedCache = distributedCache;
}
public async Task CacheResponseAsync(string cacheKey, object response, TimeSpan timeToLive)
{
if (response == null) return;
var serializedResponse = JsonConvert.SerializeObject(response);
await _distributedCache.SetStringAsync(cacheKey, serializedResponse, new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = timeToLive
});
}
public async Task<string> GetCachedResponseAsync(string cacheKey)
{
var cachedResponse = await _distributedCache.GetStringAsync(cacheKey);
return string.IsNullOrWhiteSpace(cachedResponse) ? null : cachedResponse;
}
} public class RedisCacheSettings
{
public bool Enabled { get; set; }
public string ConnectionString { get; set; }
public int DefaultSecondsToCache { get; set; }
} "RedisCacheSettings": {
"Enabled": true,
"ConnectionString": "192.168.1.107:6379,ssl=False,allowAdmin=True,abortConnect=False,defaultDatabase=0,connectTimeout=500,connectRetry=3",
"DefaultSecondsToCache": 600
}, public class CacheInstaller : IServiceInstaller
{
public void InstallServices(IServiceCollection services, AppSettings appSettings, Assembly startupProjectAssembly)
{
var redisCacheService = appSettings.RedisCacheSettings;
services.AddSingleton(redisCacheService);
if (!appSettings.RedisCacheSettings.Enabled) return;
services.AddStackExchangeRedisCache(options =>
options.Configuration = appSettings.RedisCacheSettings.ConnectionString);
// Below code applied with ISingletonDependency Interface
// services.AddSingleton<IResponseCacheService, ResponseCacheService>();
}
} [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CachedAttribute : Attribute, IAsyncActionFilter
{
private readonly int _secondsToCache;
private readonly bool _useDefaultCacheSeconds;
public CachedAttribute()
{
_useDefaultCacheSeconds = true;
}
public CachedAttribute(int secondsToCache)
{
_secondsToCache = secondsToCache;
_useDefaultCacheSeconds = false;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var cacheSettings = context.HttpContext.RequestServices.GetRequiredService<RedisCacheSettings>();
if (!cacheSettings.Enabled)
{
await next();
return;
}
var cacheService = context.HttpContext.RequestServices.GetRequiredService<IResponseCacheService>();
// Check if request has Cache
var cacheKey = GenerateCacheKeyFromRequest(context.HttpContext.Request);
var cachedResponse = await cacheService.GetCachedResponseAsync(cacheKey);
// If Yes => return Value
if (!string.IsNullOrWhiteSpace(cachedResponse))
{
var contentResult = new ContentResult
{
Content = cachedResponse,
ContentType = "application/json",
StatusCode = 200
};
context.Result = contentResult;
return;
}
// If No => Go to method => Cache Value
var actionExecutedContext = await next();
if (actionExecutedContext.Result is OkObjectResult okObjectResult)
{
var secondsToCache = _useDefaultCacheSeconds ? cacheSettings.DefaultSecondsToCache : _secondsToCache;
await cacheService.CacheResponseAsync(cacheKey, okObjectResult.Value,
TimeSpan.FromSeconds(secondsToCache));
}
}
private static string GenerateCacheKeyFromRequest(HttpRequest httpRequest)
{
var keyBuilder = new StringBuilder();
keyBuilder.Append($"{httpRequest.Path}");
foreach (var (key, value) in httpRequest.Query.OrderBy(x => x.Key))
{
keyBuilder.Append($"|{key}-{value}");
}
return keyBuilder.ToString();
}
} [Cached]
[HttpGet]
public IActionResult Get()
{
var rng = new Random();
var weatherForecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
return Ok(weatherForecasts);
} httpRequest.Query.OrderBy(x => x.Key)