caching mechanism fixes

This commit is contained in:
Hamid Reza Mohammadi 2026-09-04 19:16:04 +03:30
parent b8c521a01d
commit 4be5ec14f3
5 changed files with 76 additions and 33 deletions

View File

@ -30,6 +30,17 @@ namespace RMuseum.Controllers
[Route("api/artifacts")]
public class ArtifactController : Controller
{
/// <summary>
/// HTTP dates only have one-second precision - truncate before using a value for
/// Last-Modified/If-Modified-Since comparisons, or a DB value with sub-second precision
/// would always fail the >= comparison against a client's necessarily-truncated header
/// </summary>
/// <param name="dt"></param>
/// <returns></returns>
private static DateTime TruncateToSeconds(DateTime dt)
{
return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, dt.Kind);
}
/// <summary>
/// gets specified publish artifact item info (including images + attributes)
/// </summary>
@ -51,12 +62,12 @@ namespace RMuseum.Controllers
if (itemInfo.Result == null)
return NotFound();
Response.GetTypedHeaders().LastModified = itemInfo.Result.Item.LastModified;
Response.GetTypedHeaders().LastModified = TruncateToSeconds(itemInfo.Result.Item.LastModified);
Response.Headers.CacheControl = "public,max-age=300";
var requestHeaders = Request.GetTypedHeaders();
if (requestHeaders.IfModifiedSince.HasValue &&
requestHeaders.IfModifiedSince.Value >= itemInfo.Result.Item.LastModified)
requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(itemInfo.Result.Item.LastModified))
{
return StatusCode(StatusCodes.Status304NotModified);
}
@ -92,12 +103,12 @@ namespace RMuseum.Controllers
if (itemsInfo.Result.Items.Count() > 0)
{
DateTime lastModification = itemsInfo.Result.Items.Max(i => i.LastModified);
Response.GetTypedHeaders().LastModified = lastModification;
Response.GetTypedHeaders().LastModified = TruncateToSeconds(lastModification);
Response.Headers.CacheControl = "public,max-age=300";
var requestHeaders = Request.GetTypedHeaders();
if (requestHeaders.IfModifiedSince.HasValue &&
requestHeaders.IfModifiedSince.Value >= lastModification)
requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(lastModification))
{
return StatusCode(StatusCodes.Status304NotModified);
}
@ -185,12 +196,12 @@ namespace RMuseum.Controllers
if (itemsInfo.Result.Items.Count() > 0)
{
DateTime lastModification = itemsInfo.Result.Items.Max(i => i.LastModified);
Response.GetTypedHeaders().LastModified = lastModification;
Response.GetTypedHeaders().LastModified = TruncateToSeconds(lastModification);
Response.Headers.CacheControl = "private,max-age=300";
var requestHeaders = Request.GetTypedHeaders();
if (requestHeaders.IfModifiedSince.HasValue &&
requestHeaders.IfModifiedSince.Value >= lastModification)
requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(lastModification))
{
return StatusCode(StatusCodes.Status304NotModified);
}
@ -217,12 +228,12 @@ namespace RMuseum.Controllers
if (itemsInfo.Result.Length > 0)
{
DateTime lastModification = itemsInfo.Result.Max(i => i.LastModified);
Response.GetTypedHeaders().LastModified = lastModification;
Response.GetTypedHeaders().LastModified = TruncateToSeconds(lastModification);
Response.Headers.CacheControl = "public,max-age=300";
var requestHeaders = Request.GetTypedHeaders();
if (requestHeaders.IfModifiedSince.HasValue &&
requestHeaders.IfModifiedSince.Value >= lastModification)
requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(lastModification))
{
return StatusCode(StatusCodes.Status304NotModified);
}
@ -259,12 +270,12 @@ namespace RMuseum.Controllers
}
Response.GetTypedHeaders().LastModified = itemInfo.Result.LastModified;
Response.GetTypedHeaders().LastModified = TruncateToSeconds(itemInfo.Result.LastModified);
Response.Headers.CacheControl = "public,max-age=300";
var requestHeaders = Request.GetTypedHeaders();
if (requestHeaders.IfModifiedSince.HasValue &&
requestHeaders.IfModifiedSince.Value >= itemInfo.Result.LastModified)
requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(itemInfo.Result.LastModified))
{
return StatusCode(StatusCodes.Status304NotModified);
}
@ -300,12 +311,12 @@ namespace RMuseum.Controllers
}
Response.GetTypedHeaders().LastModified = itemInfo.Result.LastModified;
Response.GetTypedHeaders().LastModified = TruncateToSeconds(itemInfo.Result.LastModified);
Response.Headers.CacheControl = "public,max-age=300";
var requestHeaders = Request.GetTypedHeaders();
if (requestHeaders.IfModifiedSince.HasValue &&
requestHeaders.IfModifiedSince.Value >= itemInfo.Result.LastModified)
requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(itemInfo.Result.LastModified))
{
return StatusCode(StatusCodes.Status304NotModified);
}
@ -402,12 +413,12 @@ namespace RMuseum.Controllers
_memoryCache.Set(cacheKey, itemInfo, TimeSpan.FromHours(1));
}
Response.GetTypedHeaders().LastModified = itemInfo.Result.LastModified;
Response.GetTypedHeaders().LastModified = TruncateToSeconds(itemInfo.Result.LastModified);
Response.Headers.CacheControl = "public,max-age=300";
var requestHeaders = Request.GetTypedHeaders();
if (requestHeaders.IfModifiedSince.HasValue &&
requestHeaders.IfModifiedSince.Value >= itemInfo.Result.LastModified)
requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(itemInfo.Result.LastModified))
{
return StatusCode(StatusCodes.Status304NotModified);
}
@ -464,12 +475,12 @@ namespace RMuseum.Controllers
if (itemInfo.Result == null)
return NotFound();
Response.GetTypedHeaders().LastModified = itemInfo.Result.LastModified;
Response.GetTypedHeaders().LastModified = TruncateToSeconds(itemInfo.Result.LastModified);
Response.Headers.CacheControl = "private,max-age=300";
var requestHeaders = Request.GetTypedHeaders();
if (requestHeaders.IfModifiedSince.HasValue &&
requestHeaders.IfModifiedSince.Value >= itemInfo.Result.LastModified)
requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(itemInfo.Result.LastModified))
{
return StatusCode(StatusCodes.Status304NotModified);
}
@ -528,12 +539,12 @@ namespace RMuseum.Controllers
if (itemInfo.Result == null)
return NotFound();
Response.GetTypedHeaders().LastModified = itemInfo.Result.LastModified;
Response.GetTypedHeaders().LastModified = TruncateToSeconds(itemInfo.Result.LastModified);
Response.Headers.CacheControl = "private,max-age=300";
var requestHeaders = Request.GetTypedHeaders();
if (requestHeaders.IfModifiedSince.HasValue &&
requestHeaders.IfModifiedSince.Value >= itemInfo.Result.LastModified)
requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(itemInfo.Result.LastModified))
{
return StatusCode(StatusCodes.Status304NotModified);
}
@ -592,12 +603,12 @@ namespace RMuseum.Controllers
if (itemInfo.Result == null)
return NotFound();
Response.GetTypedHeaders().LastModified = itemInfo.Result.LastModified;
Response.GetTypedHeaders().LastModified = TruncateToSeconds(itemInfo.Result.LastModified);
Response.Headers.CacheControl = "private,max-age=300";
var requestHeaders = Request.GetTypedHeaders();
if (requestHeaders.IfModifiedSince.HasValue &&
requestHeaders.IfModifiedSince.Value >= itemInfo.Result.LastModified)
requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(itemInfo.Result.LastModified))
{
return StatusCode(StatusCodes.Status304NotModified);
}
@ -723,12 +734,12 @@ namespace RMuseum.Controllers
}
DateTime lastModification = lastModified.Result;
Response.GetTypedHeaders().LastModified = lastModification;
Response.GetTypedHeaders().LastModified = TruncateToSeconds(lastModification);
Response.Headers.CacheControl = "public,max-age=300";
var requestHeaders = Request.GetTypedHeaders();
if (requestHeaders.IfModifiedSince.HasValue &&
requestHeaders.IfModifiedSince.Value >= lastModification)
requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(lastModification))
{
return StatusCode(StatusCodes.Status304NotModified);
}
@ -1161,12 +1172,12 @@ namespace RMuseum.Controllers
if (itemInfo.Result == null)
return NotFound();
Response.GetTypedHeaders().LastModified = itemInfo.Result.Item.LastModified;
Response.GetTypedHeaders().LastModified = TruncateToSeconds(itemInfo.Result.Item.LastModified);
Response.Headers.CacheControl = "private,max-age=300";
var requestHeaders = Request.GetTypedHeaders();
if (requestHeaders.IfModifiedSince.HasValue &&
requestHeaders.IfModifiedSince.Value >= itemInfo.Result.Item.LastModified)
requestHeaders.IfModifiedSince.Value >= TruncateToSeconds(itemInfo.Result.Item.LastModified))
{
return StatusCode(StatusCodes.Status304NotModified);
}

View File

@ -323,6 +323,7 @@ namespace RMuseum.Controllers
return NotFound();
lastModified = img.Result.LastModified;
lastModified = new DateTime(lastModified.Year, lastModified.Month, lastModified.Day, lastModified.Hour, lastModified.Minute, lastModified.Second, lastModified.Kind);

View File

@ -51,12 +51,14 @@ namespace RMuseum.Controllers
}
Response.GetTypedHeaders().LastModified = img.Result.LastModified;
DateTime lastModified = img.Result.LastModified;
lastModified = new DateTime(lastModified.Year, lastModified.Month, lastModified.Day, lastModified.Hour, lastModified.Minute, lastModified.Second, lastModified.Kind);
Response.GetTypedHeaders().LastModified = lastModified;
Response.Headers.CacheControl = "public,max-age=86400";
var requestHeaders = Request.GetTypedHeaders();
if (requestHeaders.IfModifiedSince.HasValue &&
requestHeaders.IfModifiedSince.Value >= img.Result.LastModified)
requestHeaders.IfModifiedSince.Value >= lastModified)
{
return StatusCode(StatusCodes.Status304NotModified);
}

View File

@ -179,13 +179,15 @@ namespace RMuseum.Controllers
if (narration.Result == null)
return NotFound();
Response.GetTypedHeaders().LastModified = narration.Result.UploadDate;//TODO: Add a FileLastUpdated field to narrations to indicate the last time the mp3/xml files have been updated
DateTime lastModified = narration.Result.UploadDate;//TODO: Add a FileLastUpdated field to narrations to indicate the last time the mp3/xml files have been updated
lastModified = new DateTime(lastModified.Year, lastModified.Month, lastModified.Day, lastModified.Hour, lastModified.Minute, lastModified.Second, lastModified.Kind);
Response.GetTypedHeaders().LastModified = lastModified;
Response.Headers.CacheControl = "public,max-age=86400";
var requestHeaders = Request.GetTypedHeaders();
if (requestHeaders.IfModifiedSince.HasValue &&
requestHeaders.IfModifiedSince.Value >= narration.Result.UploadDate)
requestHeaders.IfModifiedSince.Value >= lastModified)
{
return StatusCode(StatusCodes.Status304NotModified);
}
@ -222,12 +224,14 @@ namespace RMuseum.Controllers
if (narration.Result == null)
return NotFound();
Response.GetTypedHeaders().LastModified = narration.Result.UploadDate;//TODO: Add a FileLastUpdated field to narrations to indicate the last time the mp3/xml files have been updated
DateTime lastModified = narration.Result.UploadDate;//TODO: Add a FileLastUpdated field to narrations to indicate the last time the mp3/xml files have been updated
lastModified = new DateTime(lastModified.Year, lastModified.Month, lastModified.Day, lastModified.Hour, lastModified.Minute, lastModified.Second, lastModified.Kind);
Response.GetTypedHeaders().LastModified = lastModified;
Response.Headers.CacheControl = "public,max-age=86400";
var requestHeaders = Request.GetTypedHeaders();
if (requestHeaders.IfModifiedSince.HasValue &&
requestHeaders.IfModifiedSince.Value >= narration.Result.UploadDate)
requestHeaders.IfModifiedSince.Value >= lastModified)
{
return StatusCode(StatusCodes.Status304NotModified);
}
@ -259,12 +263,14 @@ namespace RMuseum.Controllers
if (narration.Result == null)
return NotFound();
Response.GetTypedHeaders().LastModified = narration.Result.UploadDate;//TODO: Add a FileLastUpdated field to narrations to indicate the last time the mp3/xml files have been updated
DateTime lastModified = narration.Result.UploadDate;//TODO: Add a FileLastUpdated field to narrations to indicate the last time the mp3/xml files have been updated
lastModified = new DateTime(lastModified.Year, lastModified.Month, lastModified.Day, lastModified.Hour, lastModified.Minute, lastModified.Second, lastModified.Kind);
Response.GetTypedHeaders().LastModified = lastModified;
Response.Headers.CacheControl = "public,max-age=86400";
var requestHeaders = Request.GetTypedHeaders();
if (requestHeaders.IfModifiedSince.HasValue &&
requestHeaders.IfModifiedSince.Value >= narration.Result.UploadDate)
requestHeaders.IfModifiedSince.Value >= lastModified)
{
return StatusCode(StatusCodes.Status304NotModified);
}

View File

@ -41,6 +41,15 @@
<param name="imageFileService"></param>
<param name="captchaService"></param>
</member>
<member name="M:RMuseum.Controllers.ArtifactController.TruncateToSeconds(System.DateTime)">
<summary>
HTTP dates only have one-second precision - truncate before using a value for
Last-Modified/If-Modified-Since comparisons, or a DB value with sub-second precision
would always fail the >= comparison against a client's necessarily-truncated header
</summary>
<param name="dt"></param>
<returns></returns>
</member>
<member name="M:RMuseum.Controllers.ArtifactController.GetArtifactItemByFrienlyUrl(System.String,System.String)">
<summary>
gets specified publish artifact item info (including images + attributes)
@ -20761,11 +20770,25 @@
<param name="options"></param>
<returns></returns>
</member>
<member name="M:RMuseum.Services.Implementation.GanjoorService.PreparePlainText(System.Collections.Generic.List{RMuseum.Models.Ganjoor.GanjoorVerse})">
<member name="M:RMuseum.Services.Implementation.GanjoorService.ExtractSearchableTitlePart(System.String)">
<summary>
extracts the searchable part of a poem title, discarding pure boilerplate like
"غزل شمارهٔ ۱" or "بخش ۴۷" which carries no information beyond the ordinal number,
while keeping either the whole title (if it has no number at all, e.g. "طهمورث")
or just the descriptive part after the number (e.g. "بخش ۴۷ - تشبیه کردن قرآن..."
keeps "تشبیه کردن قرآن...") - returns "" when there is nothing worth indexing
</summary>
<param name="title"></param>
<returns></returns>
</member>
<member name="M:RMuseum.Services.Implementation.GanjoorService.PreparePlainText(System.Collections.Generic.List{RMuseum.Models.Ganjoor.GanjoorVerse},System.String)">
<summary>
make plain text
</summary>
<param name="verses"></param>
<param name="title">poem/section title - its searchable part (if any) is included so
titles like "بخش ۴۷ - تشبیه کردن ..." are findable through search, while pure
boilerplate like "غزل شمارهٔ ۱" is silently skipped</param>
<returns></returns>
</member>
<member name="M:RMuseum.Services.Implementation.GanjoorService.RegerneratePoemsPlainText(System.Int32)">