Server Side Caching

This commit is contained in:
Charles Showalter 2022-05-31 13:54:41 -07:00
parent dcc6396b4d
commit f6735a38fe
6 changed files with 111 additions and 0 deletions

View File

@ -24,6 +24,7 @@ namespace API.Controllers
}
[CachedAttributes(600)]
[HttpGet]
public async Task<ActionResult<IReadOnlyList<Pagination<ProductToReturnDto>>>> GetProducts([FromQuery]ProductSpecParams productParams)
{
@ -35,6 +36,7 @@ namespace API.Controllers
return Ok(new Pagination<ProductToReturnDto>(productParams.PageIndex, productParams.PageSize, totalItems, data));
}
[CachedAttributes(600)]
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse), StatusCodes.Status404NotFound)]
@ -46,12 +48,14 @@ namespace API.Controllers
return _mapper.Map<Product, ProductToReturnDto>(product);
}
[CachedAttributes(600)]
[HttpGet("brands")]
public async Task<ActionResult<IReadOnlyList<ProductBrand>>> GetProductBrands()
{
return Ok(await _productBrandRepo.ListAllAsync());
}
[CachedAttributes(600)]
[HttpGet("types")]
public async Task<ActionResult<IReadOnlyList<ProductType>>> GetProductTypes()
{

View File

@ -10,6 +10,7 @@ namespace API.Extensions
{
public static IServiceCollection AddApplicationServices(this IServiceCollection services)
{
services.AddSingleton<IResponseCacheService, ResponseCacheService>();
services.AddScoped<ITokenService, TokenService>();
services.AddScoped<iProductRepository, ProductRepository>();
services.AddScoped<IBasketRepository, BasketRepository>();

View File

@ -0,0 +1,53 @@
using System.Text;
using Core.Interfaces;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace API.Helpers
{
public class CachedAttributes : Attribute, IAsyncActionFilter
{
private readonly int _timeToLiveSeconds;
public CachedAttributes(int timeToLiveSeconds)
{
_timeToLiveSeconds = timeToLiveSeconds;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var cacheService = context.HttpContext.RequestServices.GetRequiredService<IResponseCacheService>();
var cacheKey = GenerateCacheKeyFromRequest(context.HttpContext.Request);
var cachedResponse = await cacheService.GetCachedResponseAsync(cacheKey);
if(!string.IsNullOrEmpty(cachedResponse))
{
var contentResult = new ContentResult
{
Content = cachedResponse,
ContentType = "application/json",
StatusCode = 200
};
context.Result = contentResult;
return;
}
var executedContext = await next();
if(executedContext.Result is OkObjectResult okObjectResult)
{
await cacheService.CacheResponseAsync(cacheKey, okObjectResult.Value, TimeSpan.FromSeconds(_timeToLiveSeconds));
}
}
private string GenerateCacheKeyFromRequest(HttpRequest request)
{
var keyBuilder = new StringBuilder();
keyBuilder.Append($"{request.Path}");
foreach (var (key, value) in request.Query.OrderBy(x => x.Key))
{
keyBuilder.Append($"|{key}-{value}");
}
return keyBuilder.ToString();
}
}
}

View File

@ -0,0 +1,8 @@
namespace Core.Interfaces
{
public interface IResponseCacheService
{
Task CacheResponseAsync(string cacheKey, object response, TimeSpan timeToLive);
Task<string> GetCachedResponseAsync(string cacheKey);
}
}

View File

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Core.Interfaces;
using StackExchange.Redis;
namespace Infrastructure.Services
{
public class ResponseCacheService : IResponseCacheService
{
private readonly IDatabase _database;
public ResponseCacheService(IConnectionMultiplexer redis)
{
_database = redis.GetDatabase();
}
public async Task CacheResponseAsync(string cacheKey, object response, TimeSpan timeToLive)
{
if(response == null)
{
return;
}
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
var serializedResponse = JsonSerializer.Serialize(response, options);
await _database.StringSetAsync(cacheKey, serializedResponse, timeToLive);
}
public async Task<string> GetCachedResponseAsync(string cacheKey)
{
var cachedResponse = await _database.StringGetAsync(cacheKey);
if(cachedResponse.IsNullOrEmpty){
return null;
}
return cachedResponse;
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB