aboutsummaryrefslogtreecommitdiff
path: root/Timeline/Helpers
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2020-03-13 17:05:03 +0800
committercrupest <crupest@outlook.com>2020-03-13 17:05:03 +0800
commit8833b4eb1e65dda51abd24b453b86531b3f5f7a8 (patch)
tree5e17bcec752435910ed20883ef93b84da7f55b80 /Timeline/Helpers
parent904f98bda60b3bd92331aacde3771dedde62d2b5 (diff)
downloadtimeline-8833b4eb1e65dda51abd24b453b86531b3f5f7a8.tar.gz
timeline-8833b4eb1e65dda51abd24b453b86531b3f5f7a8.tar.bz2
timeline-8833b4eb1e65dda51abd24b453b86531b3f5f7a8.zip
Abstract out data cache helper.
Diffstat (limited to 'Timeline/Helpers')
-rw-r--r--Timeline/Helpers/DataCacheHelper.cs108
1 files changed, 108 insertions, 0 deletions
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<string> GetDataETag();
+ Task<ICacheableData> GetData();
+ }
+
+ public class DelegateCacheableDataProvider : ICacheableDataProvider
+ {
+ private readonly Func<Task<string>> _getDataETagDelegate;
+ private readonly Func<Task<ICacheableData>> _getDataDelegate;
+
+ public DelegateCacheableDataProvider(Func<Task<string>> getDataETagDelegate, Func<Task<ICacheableData>> getDataDelegate)
+ {
+ _getDataETagDelegate = getDataETagDelegate;
+ _getDataDelegate = getDataDelegate;
+ }
+
+ public Task<ICacheableData> GetData()
+ {
+ return _getDataDelegate();
+ }
+
+ public Task<string> GetDataETag()
+ {
+ return _getDataETagDelegate();
+ }
+ }
+
+ public static class DataCacheHelper
+ {
+ public static async Task<ActionResult> GenerateActionResult(Controller controller, ICacheableDataProvider provider)
+ {
+ const string IfNonMatchHeaderKey = "If-None-Match";
+ const string ETagHeaderKey = "ETag";
+
+ var loggerFactory = controller.HttpContext.RequestServices.GetRequiredService<ILoggerFactory>();
+ 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<ActionResult> GenerateActionResult(Controller controller, Func<Task<string>> getDataETagDelegate, Func<Task<ICacheableData>> getDataDelegate)
+ {
+ return GenerateActionResult(controller, new DelegateCacheableDataProvider(getDataETagDelegate, getDataDelegate));
+ }
+ }
+}