You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core API如何在多租户场景下通知浏览器更新新上传logo的缓存图片

多租户logo缓存更新解决方案

方案1:基于上传时间生成URL版本指纹(推荐,可直接复用现有存储的上传日期)

这个方案实现最简单、性能最优,完全不需要额外的通知逻辑,仅靠浏览器原生缓存规则即可实现自动更新:

  • 把存储的logo上传时间转换为Unix时间戳,作为版本参数拼到logo的访问URL中,格式示例:https://your-api.com/tenant/{租户ID}/logo?v={上传时间戳}
  • 前端调用租户基础信息接口时,直接返回拼接好版本参数的logo完整URL即可。每次租户上传新logo后,上传时间更新,URL的版本参数自动变化,浏览器会将其识别为全新资源,自动发起请求拉取新logo,不会复用旧缓存
  • .NET Core API侧给logo接口配置超长强缓存即可,因为只要logo更新URL就会变,旧URL的缓存完全不会影响新logo的加载

示例代码

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

[ApiController]
[Route("api/tenant")]
public class TenantController : ControllerBase
{
    private readonly YourDbContext _dbContext;

    public TenantController(YourDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    // 返回租户基础信息,包含带版本号的logo URL
    [HttpGet("{tenantId}/info")]
    public async Task<IActionResult> GetTenantInfo(Guid tenantId)
    {
        var tenant = await _dbContext.Tenants.FindAsync(tenantId);
        if (tenant == null) return NotFound();
        
        var logoTimestamp = new DateTimeOffset(tenant.LogoUploadTime).ToUnixTimeSeconds();
        var logoUrl = $"https://your-api.com/api/tenant/{tenantId}/logo?v={logoTimestamp}";
        
        return Ok(new
        {
            TenantName = tenant.Name,
            LogoUrl = logoUrl
        });
    }

    // logo资源返回接口
    [HttpGet("{tenantId}/logo")]
    public async Task<IActionResult> GetTenantLogo(Guid tenantId, [FromQuery] long v)
    {
        var tenant = await _dbContext.Tenants.FindAsync(tenantId);
        if (tenant == null || tenant.LogoContent == null) return NotFound();
        
        // 校验版本号合法性,避免恶意请求
        var storedTimestamp = new DateTimeOffset(tenant.LogoUploadTime).ToUnixTimeSeconds();
        if (v != storedTimestamp) return BadRequest();
        
        // 设置1年强缓存,标记为不可变资源
        Response.Headers.CacheControl = "public, max-age=31536000, immutable";
        return File(tenant.LogoContent, tenant.LogoContentType);
    }
}

方案2:协商缓存实现(适用于不希望修改URL结构的场景)

如果业务要求logo的URL不能带参数,可以用HTTP协商缓存机制,利用存储的上传日期做资源校验:

  • API返回logo时,在响应头添加Last-Modified字段,值为logo上传时间的GMT标准格式
  • 浏览器后续请求logo时,会自动携带If-Modified-Since请求头,值为上一次获取的Last-Modified时间
  • API收到请求后,对比If-Modified-Since的时间和数据库中存储的上传时间:如果时间一致,直接返回304 Not Modified,浏览器复用本地缓存;如果上传时间更新,返回新的logo资源和最新的Last-Modified头

示例代码

[HttpGet("{tenantId}/logo")]
public async Task<IActionResult> GetTenantLogo(Guid tenantId)
{
    var tenant = await _dbContext.Tenants.FindAsync(tenantId);
    if (tenant == null || tenant.LogoContent == null) return NotFound();
    
    var uploadUtcTime = tenant.LogoUploadTime.ToUniversalTime();
    // 校验协商缓存请求头
    if (Request.Headers.IfModifiedSince.Any() 
        && DateTime.TryParseExact(Request.Headers.IfModifiedSince, "r", 
            System.Globalization.CultureInfo.InvariantCulture, 
            System.Globalization.DateTimeStyles.AdjustToUniversal, 
            out var requestTime) 
        && requestTime >= uploadUtcTime)
    {
        return StatusCode(StatusCodes.Status304NotModified);
    }
    
    // 设置协商缓存头
    Response.Headers.LastModified = uploadUtcTime.ToString("r");
    Response.Headers.CacheControl = "public, no-cache";
    return File(tenant.LogoContent, tenant.LogoContentType);
}

补充优化建议

  • 租户数量较多的场景下,可以把租户的logo上传时间缓存到Redis中,缓存过期时间设为1~5分钟即可,减少每次请求查询数据库的开销,即使存在极短时间的不一致,用户刷新页面即可拿到最新logo
  • 如果logo存储在对象存储服务中,上传新logo时直接覆盖旧文件,同步更新数据库的上传时间即可,上述方案逻辑完全通用
  • 多节点部署场景下不需要额外做缓存同步,两个方案的判断逻辑都基于数据库的全局一致时间,不会出现多节点缓存不一致的问题

内容的提问来源于stack exchange,提问作者Expressingx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 09:45:02