aboutsummaryrefslogtreecommitdiff
path: root/BackEnd/Timeline/Helpers
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2021-02-10 02:03:06 +0800
committercrupest <crupest@outlook.com>2021-02-10 02:03:06 +0800
commit4ea535d93753826ec900879560d876cec4d58c38 (patch)
treeb2d94c4347fddf8641fc41caa86826374c9cc241 /BackEnd/Timeline/Helpers
parentbf8f849c36bfc42cfd0ef1a66f2adf01dc8e1be7 (diff)
downloadtimeline-4ea535d93753826ec900879560d876cec4d58c38.tar.gz
timeline-4ea535d93753826ec900879560d876cec4d58c38.tar.bz2
timeline-4ea535d93753826ec900879560d876cec4d58c38.zip
...
Diffstat (limited to 'BackEnd/Timeline/Helpers')
-rw-r--r--BackEnd/Timeline/Helpers/Cache/CacheableDataDigest.cs16
-rw-r--r--BackEnd/Timeline/Helpers/Cache/DataCacheHelper.cs82
-rw-r--r--BackEnd/Timeline/Helpers/Cache/DelegateCacheableDataProvider.cs28
-rw-r--r--BackEnd/Timeline/Helpers/Cache/ICacheableDataDigest.cs10
-rw-r--r--BackEnd/Timeline/Helpers/Cache/ICacheableDataProvider.cs11
-rw-r--r--BackEnd/Timeline/Helpers/DataCacheHelper.cs125
6 files changed, 147 insertions, 125 deletions
diff --git a/BackEnd/Timeline/Helpers/Cache/CacheableDataDigest.cs b/BackEnd/Timeline/Helpers/Cache/CacheableDataDigest.cs
new file mode 100644
index 00000000..3b5bcf52
--- /dev/null
+++ b/BackEnd/Timeline/Helpers/Cache/CacheableDataDigest.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Timeline.Helpers.Cache
+{
+ public class CacheableDataDigest
+ {
+ public CacheableDataDigest(string eTag, DateTime lastModified)
+ {
+ ETag = eTag;
+ LastModified = lastModified;
+ }
+
+ public string ETag { get; set; }
+ public DateTime LastModified { get; set; }
+ }
+}
diff --git a/BackEnd/Timeline/Helpers/Cache/DataCacheHelper.cs b/BackEnd/Timeline/Helpers/Cache/DataCacheHelper.cs
new file mode 100644
index 00000000..c26bdddc
--- /dev/null
+++ b/BackEnd/Timeline/Helpers/Cache/DataCacheHelper.cs
@@ -0,0 +1,82 @@
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Net.Http.Headers;
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using Timeline.Models;
+using Timeline.Models.Http;
+
+namespace Timeline.Helpers.Cache
+{
+ public static class DataCacheHelper
+ {
+ public static async Task<ActionResult> GenerateActionResult(Controller controller, ICacheableDataProvider provider, TimeSpan? maxAge = null)
+ {
+ const string CacheControlHeaderKey = "Cache-Control";
+ const string IfNonMatchHeaderKey = "If-None-Match";
+ const string IfModifiedSinceHeaderKey = "If-Modified-Since";
+ const string ETagHeaderKey = "ETag";
+ const string LastModifiedHeaderKey = "Last-Modified";
+
+ string GenerateCacheControlHeaderValue()
+ {
+ var cacheControlHeader = new CacheControlHeaderValue()
+ {
+ NoCache = true,
+ NoStore = false,
+ MaxAge = maxAge ?? TimeSpan.FromDays(14),
+ Private = true,
+ MustRevalidate = true
+ };
+ return cacheControlHeader.ToString();
+ }
+
+ var digest = await provider.GetDigest();
+ var eTagValue = '"' + digest.ETag + '"';
+ var eTag = new EntityTagHeaderValue(eTagValue);
+
+ ActionResult Generate304Result()
+ {
+ controller.Response.Headers.Add(ETagHeaderKey, eTagValue);
+ controller.Response.Headers.Add(LastModifiedHeaderKey, digest.LastModified.ToString("R"));
+ controller.Response.Headers.Add(CacheControlHeaderKey, GenerateCacheControlHeaderValue());
+ return controller.StatusCode(StatusCodes.Status304NotModified, null);
+ }
+
+ if (controller.Request.Headers.TryGetValue(IfNonMatchHeaderKey, out var ifNonMatchHeaderValue))
+ {
+ if (!EntityTagHeaderValue.TryParseList(ifNonMatchHeaderValue, out var eTagList))
+ {
+ return controller.BadRequest(ErrorResponse.Common.Header.IfNonMatch_BadFormat());
+ }
+
+ if (eTagList.FirstOrDefault(e => e.Equals(eTag)) != null)
+ {
+ return Generate304Result();
+ }
+ }
+ else if (controller.Request.Headers.TryGetValue(IfModifiedSinceHeaderKey, out var ifModifiedSinceHeaderValue))
+ {
+ if (!DateTime.TryParse(ifModifiedSinceHeaderValue, out var headerValue))
+ {
+ return controller.BadRequest(new CommonResponse(ErrorCodes.Common.Header.IfModifiedSince_BadFormat, "Header If-Modified-Since is of bad format."));
+ }
+
+ if (headerValue > digest.LastModified)
+ {
+ return Generate304Result();
+ }
+ }
+
+ var data = await provider.GetData();
+ controller.Response.Headers.Add(CacheControlHeaderKey, GenerateCacheControlHeaderValue());
+ return controller.File(data.Data, data.ContentType, digest.LastModified, eTag);
+ }
+
+ public static Task<ActionResult> GenerateActionResult(Controller controller, Func<Task<ICacheableDataDigest>> getDigestDelegate, Func<Task<ByteData>> getDataDelegate, TimeSpan? maxAge = null)
+ {
+ return GenerateActionResult(controller, new DelegateCacheableDataProvider(getDigestDelegate, getDataDelegate), maxAge);
+ }
+ }
+}
diff --git a/BackEnd/Timeline/Helpers/Cache/DelegateCacheableDataProvider.cs b/BackEnd/Timeline/Helpers/Cache/DelegateCacheableDataProvider.cs
new file mode 100644
index 00000000..80cb66c7
--- /dev/null
+++ b/BackEnd/Timeline/Helpers/Cache/DelegateCacheableDataProvider.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Threading.Tasks;
+using Timeline.Models;
+
+namespace Timeline.Helpers.Cache
+{
+ public class DelegateCacheableDataProvider : ICacheableDataProvider
+ {
+ private readonly Func<Task<ICacheableDataDigest>> _getDigestDelegate;
+ private readonly Func<Task<ByteData>> _getDataDelegate;
+
+ public DelegateCacheableDataProvider(Func<Task<ICacheableDataDigest>> getDigestDelegate, Func<Task<ByteData>> getDataDelegate)
+ {
+ _getDigestDelegate = getDigestDelegate;
+ _getDataDelegate = getDataDelegate;
+ }
+
+ public Task<ICacheableDataDigest> GetDigest()
+ {
+ return _getDigestDelegate();
+ }
+
+ public Task<ByteData> GetData()
+ {
+ return _getDataDelegate();
+ }
+ }
+}
diff --git a/BackEnd/Timeline/Helpers/Cache/ICacheableDataDigest.cs b/BackEnd/Timeline/Helpers/Cache/ICacheableDataDigest.cs
new file mode 100644
index 00000000..32519d7e
--- /dev/null
+++ b/BackEnd/Timeline/Helpers/Cache/ICacheableDataDigest.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Timeline.Helpers.Cache
+{
+ public interface ICacheableDataDigest
+ {
+ string ETag { get; }
+ DateTime LastModified { get; }
+ }
+}
diff --git a/BackEnd/Timeline/Helpers/Cache/ICacheableDataProvider.cs b/BackEnd/Timeline/Helpers/Cache/ICacheableDataProvider.cs
new file mode 100644
index 00000000..b270fb1d
--- /dev/null
+++ b/BackEnd/Timeline/Helpers/Cache/ICacheableDataProvider.cs
@@ -0,0 +1,11 @@
+using System.Threading.Tasks;
+using Timeline.Models;
+
+namespace Timeline.Helpers.Cache
+{
+ public interface ICacheableDataProvider
+ {
+ Task<ICacheableDataDigest> GetDigest();
+ Task<ByteData> GetData();
+ }
+}
diff --git a/BackEnd/Timeline/Helpers/DataCacheHelper.cs b/BackEnd/Timeline/Helpers/DataCacheHelper.cs
deleted file mode 100644
index 1ad69708..00000000
--- a/BackEnd/Timeline/Helpers/DataCacheHelper.cs
+++ /dev/null
@@ -1,125 +0,0 @@
-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, TimeSpan? maxAge = null)
- {
- const string CacheControlHeaderKey = "Cache-Control";
- const string IfNonMatchHeaderKey = "If-None-Match";
- const string ETagHeaderKey = "ETag";
-
- string GenerateCacheControlHeaderValue()
- {
- var cacheControlHeader = new CacheControlHeaderValue()
- {
- NoCache = true,
- NoStore = false,
- MaxAge = maxAge ?? TimeSpan.FromDays(14),
- Private = true,
- MustRevalidate = true
- };
- return cacheControlHeader.ToString();
- }
-
- 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)
- {
- logger.LogInformation(LogResultNotModified);
- controller.Response.Headers.Add(ETagHeaderKey, eTagValue);
- controller.Response.Headers.Add(CacheControlHeaderKey, GenerateCacheControlHeaderValue());
-
- return controller.StatusCode(StatusCodes.Status304NotModified, null);
- }
- }
-
- var data = await provider.GetData();
- logger.LogInformation(LogResultData);
- controller.Response.Headers.Add(CacheControlHeaderKey, GenerateCacheControlHeaderValue());
- 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, TimeSpan? maxAge = null)
- {
- return GenerateActionResult(controller, new DelegateCacheableDataProvider(getDataETagDelegate, getDataDelegate), maxAge);
- }
- }
-}