ASP.NET Core Web API 检查 if-modified-since header 不工作因为浏览器甚至不提交给服务器
ASP.NET Core Web API checking if-modified-since header not working because browser does not even submit to server
我有一个 return 是 FileStreamResult
的控制器。部分处理会查看 'if-modified-since' header 是否小于数据库中的当前日期 return NotModified()
或实际 FileStreamResult
。问题是,客户端(在本例中为 Swagger)甚至没有提交到我的服务器,因此我可以根据传入的 header 检查数据库日期。我没有在 Startup
中设置任何 'caching' 信息。鉴于下面的代码,任何想法如何确保它提交回我的网站 api 以便我可以进行数据库比较?
这是 Chrome 网络选项卡的屏幕截图。你可以看到第一个(显示为 test)提交到服务器(我打开了禁用缓存),然后再次提交并由 (磁盘缓存)提供服务 - 从不访问服务器。
注意,我按照这些链接作为模板:
- https://rehansaeed.com/asp-net-core-caching-in-practice/#last-modified--if-modified-since
- https://www.geekytidbits.com/efficient-caching-dynamic-resources-asp-net-304-not-modified/
[ApiController]
public class DownloadSpecificVersion : ControllerBase
{
private readonly IDbConnectionFactory dbConnectionFactory;
public DownloadSpecificVersion( IDbConnectionFactory dbConnectionFactory ) => this.dbConnectionFactory = dbConnectionFactory;
public record Parameters
{
private string _folder;
[FromRoute]
public string Folder
{
get => _folder.KatAppFolder();
set => _folder = value;
}
[FromRoute]
public string Name { get; init; }
[FromRoute]
public string Version { get; init; }
}
[HttpGet( "/api/kat-apps/{folder}/{name}/version/{version}" )]
[SwaggerOperation(
Summary = "Download specific version of KatApp kaml file",
Description = "Download specific version of KatApp kaml file",
OperationId = "KatApps.DownloadSpecificVersion",
Tags = new[] { "KatApps" } )]
[ProducesResponseType( typeof( FileStreamResult ), StatusCodes.Status200OK )]
[ProducesResponseType( StatusCodes.Status304NotModified )]
[ProducesResponseType( typeof( ValidationProblemDetails ), StatusCodes.Status401Unauthorized )]
[ProducesResponseType( typeof( ValidationProblemDetails ), StatusCodes.Status404NotFound )]
public async Task<IActionResult> HandleAsync( [FromQuery] Parameters parameters )
{
using ( var cn = await dbConnectionFactory.CreateDataLockerConnectionAsync() )
{
var keyInfo = await cn.QueryBuilder( $@"omitted, query to find item in db" ).QueryFirstOrDefaultAsync<CacheDownloadInfo>();
return keyInfo != null
? await CachedOrModifiedAsync( keyInfo, dbConnectionFactory )
: NotFound();
}
}
}
protected async Task<IActionResult> CachedOrModifiedAsync( CacheDownloadInfo cacheDownloadInfo, IDbConnectionFactory dbConnectionFactory )
{
var lastModifiedDate = cacheDownloadInfo.LastModifiedDate.Value.ToUniversalTime();
// https://rehansaeed.com/asp-net-core-caching-in-practice/#last-modified--if-modified-since
// https://www.geekytidbits.com/efficient-caching-dynamic-resources-asp-net-304-not-modified/
var requestHeaders = HttpContext.Request.GetTypedHeaders();
// HTTP does not provide milliseconds, so remove it from the comparison
if ( requestHeaders.IfModifiedSince.HasValue && lastModifiedDate.AddMilliseconds( -lastModifiedDate.Millisecond ) <= requestHeaders.IfModifiedSince.Value )
{
return NotModified();
}
var responseHeaders = HttpContext.Response.GetTypedHeaders();
responseHeaders.LastModified = lastModifiedDate;
var fs = File.OpenRead( "c:\test.txt" ); // This is really code that gets a 'stream' from the database
return new FileStreamResult( fs, "text/plain" );
}
在此 if 语句中,您正在比较日期时间以获得完全匹配 ==
而不是 >=
比较,因此大多数时候您可能会得到未修改的响应.
if (lastModifiedDate.AddMilliseconds(-lastModifiedDate.Millisecond) ==
requestHeaders.IfModifiedSince.Value)
{
return NotModified();
}
这个比较应该更像这样:
if (requestHeaders.IfModifiedSince.HasValue &&
requestHeaders.IfModifiedSince.Value >= lastModifiedDate)
{
return NotModified();
}
我的问题是我在浏览器控制台中通过简单的 $.ajax()
调用对此进行了测试。我需要在配置中设置 ifModified: true
以便它使用 if-modified-since
header.
调用服务器
我在使用 Swagger 时发现了另一个问题。它似乎只有在我设置以下内容时才有效:
responseHeaders.CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue
{
Public = true,
MustRevalidate = true,
MaxAge = new TimeSpan( 0, 0, 0 ),
};
responseHeaders.Expires = DateTime.UtcNow;
这将导致:
Cache-Control:public, must-revalidate, max-age=0
Last-Modified:Sun, 10 Jun 2012 20:19:21 GMT
我有一个 return 是 FileStreamResult
的控制器。部分处理会查看 'if-modified-since' header 是否小于数据库中的当前日期 return NotModified()
或实际 FileStreamResult
。问题是,客户端(在本例中为 Swagger)甚至没有提交到我的服务器,因此我可以根据传入的 header 检查数据库日期。我没有在 Startup
中设置任何 'caching' 信息。鉴于下面的代码,任何想法如何确保它提交回我的网站 api 以便我可以进行数据库比较?
这是 Chrome 网络选项卡的屏幕截图。你可以看到第一个(显示为 test)提交到服务器(我打开了禁用缓存),然后再次提交并由 (磁盘缓存)提供服务 - 从不访问服务器。
注意,我按照这些链接作为模板:
- https://rehansaeed.com/asp-net-core-caching-in-practice/#last-modified--if-modified-since
- https://www.geekytidbits.com/efficient-caching-dynamic-resources-asp-net-304-not-modified/
[ApiController]
public class DownloadSpecificVersion : ControllerBase
{
private readonly IDbConnectionFactory dbConnectionFactory;
public DownloadSpecificVersion( IDbConnectionFactory dbConnectionFactory ) => this.dbConnectionFactory = dbConnectionFactory;
public record Parameters
{
private string _folder;
[FromRoute]
public string Folder
{
get => _folder.KatAppFolder();
set => _folder = value;
}
[FromRoute]
public string Name { get; init; }
[FromRoute]
public string Version { get; init; }
}
[HttpGet( "/api/kat-apps/{folder}/{name}/version/{version}" )]
[SwaggerOperation(
Summary = "Download specific version of KatApp kaml file",
Description = "Download specific version of KatApp kaml file",
OperationId = "KatApps.DownloadSpecificVersion",
Tags = new[] { "KatApps" } )]
[ProducesResponseType( typeof( FileStreamResult ), StatusCodes.Status200OK )]
[ProducesResponseType( StatusCodes.Status304NotModified )]
[ProducesResponseType( typeof( ValidationProblemDetails ), StatusCodes.Status401Unauthorized )]
[ProducesResponseType( typeof( ValidationProblemDetails ), StatusCodes.Status404NotFound )]
public async Task<IActionResult> HandleAsync( [FromQuery] Parameters parameters )
{
using ( var cn = await dbConnectionFactory.CreateDataLockerConnectionAsync() )
{
var keyInfo = await cn.QueryBuilder( $@"omitted, query to find item in db" ).QueryFirstOrDefaultAsync<CacheDownloadInfo>();
return keyInfo != null
? await CachedOrModifiedAsync( keyInfo, dbConnectionFactory )
: NotFound();
}
}
}
protected async Task<IActionResult> CachedOrModifiedAsync( CacheDownloadInfo cacheDownloadInfo, IDbConnectionFactory dbConnectionFactory )
{
var lastModifiedDate = cacheDownloadInfo.LastModifiedDate.Value.ToUniversalTime();
// https://rehansaeed.com/asp-net-core-caching-in-practice/#last-modified--if-modified-since
// https://www.geekytidbits.com/efficient-caching-dynamic-resources-asp-net-304-not-modified/
var requestHeaders = HttpContext.Request.GetTypedHeaders();
// HTTP does not provide milliseconds, so remove it from the comparison
if ( requestHeaders.IfModifiedSince.HasValue && lastModifiedDate.AddMilliseconds( -lastModifiedDate.Millisecond ) <= requestHeaders.IfModifiedSince.Value )
{
return NotModified();
}
var responseHeaders = HttpContext.Response.GetTypedHeaders();
responseHeaders.LastModified = lastModifiedDate;
var fs = File.OpenRead( "c:\test.txt" ); // This is really code that gets a 'stream' from the database
return new FileStreamResult( fs, "text/plain" );
}
在此 if 语句中,您正在比较日期时间以获得完全匹配 ==
而不是 >=
比较,因此大多数时候您可能会得到未修改的响应.
if (lastModifiedDate.AddMilliseconds(-lastModifiedDate.Millisecond) ==
requestHeaders.IfModifiedSince.Value)
{
return NotModified();
}
这个比较应该更像这样:
if (requestHeaders.IfModifiedSince.HasValue &&
requestHeaders.IfModifiedSince.Value >= lastModifiedDate)
{
return NotModified();
}
我的问题是我在浏览器控制台中通过简单的 $.ajax()
调用对此进行了测试。我需要在配置中设置 ifModified: true
以便它使用 if-modified-since
header.
我在使用 Swagger 时发现了另一个问题。它似乎只有在我设置以下内容时才有效:
responseHeaders.CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue
{
Public = true,
MustRevalidate = true,
MaxAge = new TimeSpan( 0, 0, 0 ),
};
responseHeaders.Expires = DateTime.UtcNow;
这将导致:
Cache-Control:public, must-revalidate, max-age=0 Last-Modified:Sun, 10 Jun 2012 20:19:21 GMT