From 4ea535d93753826ec900879560d876cec4d58c38 Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 10 Feb 2021 02:03:06 +0800 Subject: ... --- BackEnd/Timeline/Helpers/Cache/DataCacheHelper.cs | 82 +++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 BackEnd/Timeline/Helpers/Cache/DataCacheHelper.cs (limited to 'BackEnd/Timeline/Helpers/Cache/DataCacheHelper.cs') 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 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 GenerateActionResult(Controller controller, Func> getDigestDelegate, Func> getDataDelegate, TimeSpan? maxAge = null) + { + return GenerateActionResult(controller, new DelegateCacheableDataProvider(getDigestDelegate, getDataDelegate), maxAge); + } + } +} -- cgit v1.2.3