From 8833b4eb1e65dda51abd24b453b86531b3f5f7a8 Mon Sep 17 00:00:00 2001 From: crupest Date: Fri, 13 Mar 2020 17:05:03 +0800 Subject: Abstract out data cache helper. --- Timeline/Helpers/DataCacheHelper.cs | 108 ++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 Timeline/Helpers/DataCacheHelper.cs (limited to 'Timeline/Helpers/DataCacheHelper.cs') diff --git a/Timeline/Helpers/DataCacheHelper.cs b/Timeline/Helpers/DataCacheHelper.cs new file mode 100644 index 00000000..c13aaddb --- /dev/null +++ b/Timeline/Helpers/DataCacheHelper.cs @@ -0,0 +1,108 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Net.Http.Headers; +using System; +using System.Linq; +using System.Threading.Tasks; +using Timeline.Models.Http; +using static Timeline.Resources.Helper.DataCacheHelper; + +namespace Timeline.Helpers +{ + public interface ICacheableData + { + string Type { get; } +#pragma warning disable CA1819 // Properties should not return arrays + byte[] Data { get; } +#pragma warning restore CA1819 // Properties should not return arrays + DateTime? LastModified { get; } + } + + public class CacheableData : ICacheableData + { + public CacheableData(string type, byte[] data, DateTime? lastModified) + { + Type = type; + Data = data; + LastModified = lastModified; + } + + public string Type { get; set; } +#pragma warning disable CA1819 // Properties should not return arrays + public byte[] Data { get; set; } +#pragma warning restore CA1819 // Properties should not return arrays + public DateTime? LastModified { get; set; } + } + + public interface ICacheableDataProvider + { + Task GetDataETag(); + Task GetData(); + } + + public class DelegateCacheableDataProvider : ICacheableDataProvider + { + private readonly Func> _getDataETagDelegate; + private readonly Func> _getDataDelegate; + + public DelegateCacheableDataProvider(Func> getDataETagDelegate, Func> getDataDelegate) + { + _getDataETagDelegate = getDataETagDelegate; + _getDataDelegate = getDataDelegate; + } + + public Task GetData() + { + return _getDataDelegate(); + } + + public Task GetDataETag() + { + return _getDataETagDelegate(); + } + } + + public static class DataCacheHelper + { + public static async Task GenerateActionResult(Controller controller, ICacheableDataProvider provider) + { + const string IfNonMatchHeaderKey = "If-None-Match"; + const string ETagHeaderKey = "ETag"; + + var loggerFactory = controller.HttpContext.RequestServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger(typeof(DataCacheHelper)); + + var eTagValue = await provider.GetDataETag(); + eTagValue = '"' + eTagValue + '"'; + var eTag = new EntityTagHeaderValue(eTagValue); + + + if (controller.Request.Headers.TryGetValue(IfNonMatchHeaderKey, out var value)) + { + if (!EntityTagHeaderValue.TryParseStrictList(value, out var eTagList)) + { + logger.LogInformation(Log.Format(LogBadIfNoneMatch, ("Header Value", value))); + return controller.BadRequest(ErrorResponse.Common.Header.IfNonMatch_BadFormat()); + } + + if (eTagList.FirstOrDefault(e => e.Equals(eTag)) != null) + { + controller.Response.Headers.Add(ETagHeaderKey, eTagValue); + logger.LogInformation(LogResultNotModified); + return controller.StatusCode(StatusCodes.Status304NotModified); + } + } + + var data = await provider.GetData(); + logger.LogInformation(LogResultData); + return controller.File(data.Data, data.Type, data.LastModified, eTag); + } + + public static Task GenerateActionResult(Controller controller, Func> getDataETagDelegate, Func> getDataDelegate) + { + return GenerateActionResult(controller, new DelegateCacheableDataProvider(getDataETagDelegate, getDataDelegate)); + } + } +} -- cgit v1.2.3