简体   繁体   中英

ASP.NET Core Web API checking if-modified-since header not working because browser does not even submit to server

I have a controller that returns a FileStreamResult . Part of the processing looks to see if the 'if-modified-since' header is less that current date in the database to return either NotModified() or the actual FileStreamResult . The problem is, the client (in this case Swagger) doesn't even submit to my server so I can check the database date against the passed in header. I haven't set any 'caching' information in Startup . Given the code below, any ideas how to make sure it submits back to my web api so I can do the database compare?

Here is screenshot of Chrome network tab. You can see the first one (displayed as test ) submitted to server (I had disable cache turned on), then submitted again and it was served by (disk cache) - never hitting the server.

在此处输入图像描述

Note, I followed these links as my template:

  1. https://rehansaeed.com/asp-net-core-caching-in-practice/#last-modified--if-modified-since
  2. 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" );
}

My issue was I was testing this in browser console with simple $.ajax() call. I needed to set the ifModified: true in the configuration for it call the server with the if-modified-since header.

I discovered another issue when using Swagger. It only seemed to work if I set the following:

responseHeaders.CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue
{
    Public = true,
    MustRevalidate = true,
    MaxAge = new TimeSpan( 0, 0, 0 ),
};
responseHeaders.Expires = DateTime.UtcNow;

This will result in:

Cache-Control:public, must-revalidate, max-age=0 Last-Modified:Sun, 10 Jun 2012 20:19:21 GMT

In this if-statement, you are comparing a datetime for an exact match == instead of a >= comparison, so most of the time you are probably going to get a not modified response.

if (lastModifiedDate.AddMilliseconds(-lastModifiedDate.Millisecond) == 
    requestHeaders.IfModifiedSince.Value)
{
    return NotModified();
}

This comparison should be more like this:

if (requestHeaders.IfModifiedSince.HasValue &&
    requestHeaders.IfModifiedSince.Value >= lastModifiedDate)
{
    return NotModified();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM