diff --git a/RMuseum/Controllers/ArtifactController.cs b/RMuseum/Controllers/ArtifactController.cs index f68f72b5..4468c089 100644 --- a/RMuseum/Controllers/ArtifactController.cs +++ b/RMuseum/Controllers/ArtifactController.cs @@ -30,6 +30,17 @@ namespace RMuseum.Controllers [Route("api/artifacts")] public class ArtifactController : Controller { + /// + /// 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 + /// + /// + /// + private static DateTime TruncateToSeconds(DateTime dt) + { + return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, dt.Kind); + } /// /// gets specified publish artifact item info (including images + attributes) /// @@ -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); } diff --git a/RMuseum/Controllers/GanjoorController.cs b/RMuseum/Controllers/GanjoorController.cs index 0fcf06fe..fb25aa9a 100644 --- a/RMuseum/Controllers/GanjoorController.cs +++ b/RMuseum/Controllers/GanjoorController.cs @@ -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); diff --git a/RMuseum/Controllers/ImageController.cs b/RMuseum/Controllers/ImageController.cs index c6bc2d27..29fb767c 100644 --- a/RMuseum/Controllers/ImageController.cs +++ b/RMuseum/Controllers/ImageController.cs @@ -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); } diff --git a/RMuseum/Controllers/RecitationController.cs b/RMuseum/Controllers/RecitationController.cs index afa2ef97..074fc304 100644 --- a/RMuseum/Controllers/RecitationController.cs +++ b/RMuseum/Controllers/RecitationController.cs @@ -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); } diff --git a/RMuseum/RMuseum.xml b/RMuseum/RMuseum.xml index 266b2ab4..73a10c21 100644 --- a/RMuseum/RMuseum.xml +++ b/RMuseum/RMuseum.xml @@ -41,6 +41,15 @@ + + + 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 + + + + gets specified publish artifact item info (including images + attributes) @@ -20761,11 +20770,25 @@ - + + + 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 + + + + + make plain text + poem/section title - its searchable part (if any) is included so + titles like "بخش ۴۷ - تشبیه کردن ..." are findable through search, while pure + boilerplate like "غزل شمارهٔ ۱" is silently skipped