diff --git a/src/TerribleDev.Blog.Web/Extensions/ArrayExtensions.cs b/src/TerribleDev.Blog.Web/Extensions/ArrayExtensions.cs new file mode 100644 index 0000000..2eae3f4 --- /dev/null +++ b/src/TerribleDev.Blog.Web/Extensions/ArrayExtensions.cs @@ -0,0 +1,12 @@ +using System; + +namespace TerribleDev.Blog.Web +{ + public static class ArrayExtensions + { + public static string ToHexString(this byte[] bytes) + { + return Convert.ToHexString(bytes); + } + } +} \ No newline at end of file diff --git a/src/TerribleDev.Blog.Web/Factories/BlogFactory.cs b/src/TerribleDev.Blog.Web/Factories/BlogFactory.cs index b7b7c33..935e322 100644 --- a/src/TerribleDev.Blog.Web/Factories/BlogFactory.cs +++ b/src/TerribleDev.Blog.Web/Factories/BlogFactory.cs @@ -15,6 +15,8 @@ using System.Collections.Concurrent; using Schema.NET; using System.Text.RegularExpressions; using TerribleDev.Blog.Web.Factories; +using System.Text; +using System.Security.Cryptography; namespace TerribleDev.Blog.Web { @@ -123,7 +125,8 @@ namespace TerribleDev.Blog.Web JsonLDString = ld.ToHtmlEscapedString().Replace("https://schema.org", "https://schema.org/true"), JsonLDBreadcrumb = breadcrumb, JsonLDBreadcrumbString = breadcrumb.ToHtmlEscapedString().Replace("https://schema.org", "https://schema.org/true"), - HasCode = hasCode + HasCode = hasCode, + MarkdownMD5 = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(markdownText)).ToHexString() }; var thumbNailUrl = string.IsNullOrWhiteSpace(postSettings.thumbnailImage) ? postImages?.FirstOrDefault() ?? "https://www.gravatar.com/avatar/333e3cea32cd17ff2007d131df336061?s=640" : @@ -176,7 +179,8 @@ namespace TerribleDev.Blog.Web SummaryPlainShort = (postContentPlain.Length <= 147 ? postContentPlain : postContentPlain.Substring(0, 146)) + "...", JsonLDBreadcrumb = breadcrumb, JsonLDBreadcrumbString = breadcrumb.ToHtmlEscapedString().Replace("https://schema.org", "https://schema.org/true"), - HasCode = hasCode + HasCode = hasCode, + MarkdownMD5 = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(markdownText)).ToHexString() }; return new LandingPage() { diff --git a/src/TerribleDev.Blog.Web/Filters/ETagFilter.cs b/src/TerribleDev.Blog.Web/Filters/ETagFilter.cs new file mode 100644 index 0000000..e9ecd84 --- /dev/null +++ b/src/TerribleDev.Blog.Web/Filters/ETagFilter.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Concurrent; +using System.Security.Cryptography; +using System.Text; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace TerribleDev.Blog.Web.Filters +{ + public class StaticETag: ActionFilterAttribute + { + public static string staticEtag = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(DateTimeOffset.Now.ToUnixTimeMilliseconds().ToString())).ToHexString().Substring(0,8); + public static ConcurrentDictionary cache = new ConcurrentDictionary(); + public override void OnActionExecuting(ActionExecutingContext context) + { + var etag = context.HttpContext.Request.Headers["If-None-Match"]; + if(etag == staticEtag) + { + context.Result = new StatusCodeResult(304); + } + else + { + base.OnActionExecuting(context); + } + } + public override void OnActionExecuted(ActionExecutedContext context) + { + if(context.HttpContext.Response.StatusCode >= 200 && context.HttpContext.Response.StatusCode < 300 && context.HttpContext.Response.Headers.ETag.Count == 0) + { + context.HttpContext.Response.Headers.Add("ETag", staticEtag); + } + } + } +} \ No newline at end of file diff --git a/src/TerribleDev.Blog.Web/Models/IPostContent.cs b/src/TerribleDev.Blog.Web/Models/IPostContent.cs index 6d6ee73..bfb76cc 100644 --- a/src/TerribleDev.Blog.Web/Models/IPostContent.cs +++ b/src/TerribleDev.Blog.Web/Models/IPostContent.cs @@ -22,5 +22,6 @@ namespace TerribleDev.Blog.Web.Models public string JsonLDString { get; set; } BreadcrumbList JsonLDBreadcrumb { get; set; } string JsonLDBreadcrumbString { get; set; } + string MarkdownMD5 { get; set; } } } diff --git a/src/TerribleDev.Blog.Web/Models/PostContent.cs b/src/TerribleDev.Blog.Web/Models/PostContent.cs index 59b8eb9..ccbeb52 100644 --- a/src/TerribleDev.Blog.Web/Models/PostContent.cs +++ b/src/TerribleDev.Blog.Web/Models/PostContent.cs @@ -20,5 +20,6 @@ namespace TerribleDev.Blog.Web.Models public BreadcrumbList JsonLDBreadcrumb { get; set; } public string JsonLDBreadcrumbString { get; set; } public bool HasCode { get; set; } + public string MarkdownMD5 { get; set; } } } \ No newline at end of file diff --git a/src/TerribleDev.Blog.Web/Startup.cs b/src/TerribleDev.Blog.Web/Startup.cs index 8189756..e70f464 100644 --- a/src/TerribleDev.Blog.Web/Startup.cs +++ b/src/TerribleDev.Blog.Web/Startup.cs @@ -14,6 +14,7 @@ using TerribleDev.Blog.Web.Factories; using Microsoft.Extensions.Hosting; using WebMarkupMin.AspNetCore6; using Microsoft.Extensions.Logging; +using TerribleDev.Blog.Web.Filters; namespace TerribleDev.Blog.Web { @@ -58,7 +59,9 @@ namespace TerribleDev.Blog.Web } return postCache; }); - var controllerBuilder = services.AddControllersWithViews(); + var controllerBuilder = services.AddControllersWithViews(a => { + a.Filters.Add(new StaticETag()); + }); #if DEBUG if (Env.IsDevelopment()) { diff --git a/src/TerribleDev.Blog.Web/appsettings.Development.json b/src/TerribleDev.Blog.Web/appsettings.Development.json index e203e94..6cf0b42 100644 --- a/src/TerribleDev.Blog.Web/appsettings.Development.json +++ b/src/TerribleDev.Blog.Web/appsettings.Development.json @@ -1,9 +1,7 @@ { "Logging": { "LogLevel": { - "Default": "Debug", - "System": "Information", - "Microsoft": "Information" + "Default": "Warning" } } }