diff options
author | crupest <crupest@outlook.com> | 2020-10-27 19:21:35 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2020-10-27 19:21:35 +0800 |
commit | ac769e656b122ff569c3f1534701b71e00fed586 (patch) | |
tree | 72966645ff1e25139d3995262e1c4349f2c14733 /BackEnd/Timeline/Helpers | |
parent | 14e5848c23c643cea9b5d709770747d98c3d75e2 (diff) | |
download | timeline-ac769e656b122ff569c3f1534701b71e00fed586.tar.gz timeline-ac769e656b122ff569c3f1534701b71e00fed586.tar.bz2 timeline-ac769e656b122ff569c3f1534701b71e00fed586.zip |
Split front and back end.
Diffstat (limited to 'BackEnd/Timeline/Helpers')
-rw-r--r-- | BackEnd/Timeline/Helpers/DataCacheHelper.cs | 125 | ||||
-rw-r--r-- | BackEnd/Timeline/Helpers/DateTimeExtensions.cs | 14 | ||||
-rw-r--r-- | BackEnd/Timeline/Helpers/InvalidModelResponseFactory.cs | 25 | ||||
-rw-r--r-- | BackEnd/Timeline/Helpers/LanguageHelper.cs | 12 | ||||
-rw-r--r-- | BackEnd/Timeline/Helpers/Log.cs | 22 |
5 files changed, 198 insertions, 0 deletions
diff --git a/BackEnd/Timeline/Helpers/DataCacheHelper.cs b/BackEnd/Timeline/Helpers/DataCacheHelper.cs new file mode 100644 index 00000000..1ad69708 --- /dev/null +++ b/BackEnd/Timeline/Helpers/DataCacheHelper.cs @@ -0,0 +1,125 @@ +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);
+ }
+ }
+}
diff --git a/BackEnd/Timeline/Helpers/DateTimeExtensions.cs b/BackEnd/Timeline/Helpers/DateTimeExtensions.cs new file mode 100644 index 00000000..374f3bc9 --- /dev/null +++ b/BackEnd/Timeline/Helpers/DateTimeExtensions.cs @@ -0,0 +1,14 @@ +using System;
+
+namespace Timeline.Helpers
+{
+ public static class DateTimeExtensions
+ {
+ public static DateTime MyToUtc(this DateTime dateTime)
+ {
+ if (dateTime.Kind == DateTimeKind.Utc) return dateTime;
+ if (dateTime.Kind == DateTimeKind.Local) return dateTime.ToUniversalTime();
+ return DateTime.SpecifyKind(dateTime, DateTimeKind.Utc);
+ }
+ }
+}
diff --git a/BackEnd/Timeline/Helpers/InvalidModelResponseFactory.cs b/BackEnd/Timeline/Helpers/InvalidModelResponseFactory.cs new file mode 100644 index 00000000..9b253e7d --- /dev/null +++ b/BackEnd/Timeline/Helpers/InvalidModelResponseFactory.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Mvc;
+using System.Text;
+using Timeline.Models.Http;
+
+namespace Timeline.Helpers
+{
+ public static class InvalidModelResponseFactory
+ {
+ public static IActionResult Factory(ActionContext context)
+ {
+ var modelState = context.ModelState;
+
+ var messageBuilder = new StringBuilder();
+ foreach (var model in modelState)
+ foreach (var error in model.Value.Errors)
+ {
+ messageBuilder.Append(model.Key);
+ messageBuilder.Append(" : ");
+ messageBuilder.AppendLine(error.ErrorMessage);
+ }
+
+ return new BadRequestObjectResult(ErrorResponse.Common.CustomMessage_InvalidModel(messageBuilder.ToString()));
+ }
+ }
+}
diff --git a/BackEnd/Timeline/Helpers/LanguageHelper.cs b/BackEnd/Timeline/Helpers/LanguageHelper.cs new file mode 100644 index 00000000..b0156b8b --- /dev/null +++ b/BackEnd/Timeline/Helpers/LanguageHelper.cs @@ -0,0 +1,12 @@ +using System.Linq;
+
+namespace Timeline.Helpers
+{
+ public static class LanguageHelper
+ {
+ public static bool AreSame(this bool firstBool, params bool[] otherBools)
+ {
+ return otherBools.All(b => b == firstBool);
+ }
+ }
+}
diff --git a/BackEnd/Timeline/Helpers/Log.cs b/BackEnd/Timeline/Helpers/Log.cs new file mode 100644 index 00000000..af0b7e13 --- /dev/null +++ b/BackEnd/Timeline/Helpers/Log.cs @@ -0,0 +1,22 @@ +using System.Text;
+
+namespace Timeline.Helpers
+{
+ public static class Log
+ {
+ public static string Format(string summary, params (string, object?)[] properties)
+ {
+ var builder = new StringBuilder();
+ builder.Append(summary);
+ foreach (var property in properties)
+ {
+ var (key, value) = property;
+ builder.AppendLine();
+ builder.Append(key);
+ builder.Append(" : ");
+ builder.Append(value);
+ }
+ return builder.ToString();
+ }
+ }
+}
|