老代码(ASP.NET MVC 5 / .NET Framework 4.x)

1. 页面级 OutputCache

[OutputCache(Duration = 10, VaryByParam = "none")]
public ActionResult Index() { ... }

// 或在 web.config 集中配置
// <caching>
//   <outputCacheProfiles>
//     <add name="sample" duration="3600" varyByParam="none" />
//   </outputCacheProfiles>
// </caching>
[OutputCache(CacheProfile = "sample")]
public ActionResult List() { ... }

局限:必须用 /home/index 这种 query string 形式,纯 /home 不会缓存。

2. web.config 文件上传限制

<system.web>
  <httpRuntime targetFramework="4.5"
               maxRequestLength="2147483647"
               executionTimeout="36000" />
</system.web>

含义:上传单文件最长约 2 GB(maxRequestLength 单位 KB),执行超时 10 小时。

双重隐患: 1. ASP.NET Core 已不再用 web.config 配置运行时行为,需要移到 appsettings.json + Kestrel 配置。 2. maxRequestLength="2147483647" 看似无上限,实际会先撞到 IIS 的 maxAllowedContentLength(默认 30 MB),返回 404.13。

ASP.NET Core 8 替代方案

3. ResponseCaching 替代 OutputCache(响应级缓存)

// Program.cs
builder.Services.AddResponseCaching(options =>
{
    options.MaximumBodySize = 64 * 1024 * 1024;  // 64 MB,可缓存的最大响应体
    options.SizeLimit = 256 * 1024 * 1024;       // 缓存池总大小
    options.UseCaseSensitivePaths = false;
});

// 控制器上
[ResponseCache(Duration = 60, VaryByHeader = "User-Agent")]
public IActionResult Index() { ... }

特点:

  • 遵循 HTTP 标准的 Cache-Control 头,浏览器、CDN、反代都能识别。
  • 需要 app.UseResponseCaching() 中间件介入。
  • 默认仅缓存 GET + 200 + 有 Cache-Control: public 的响应。

4. IDistributedCache 替代 OutputCache(共享缓存)

需要多实例共享时,用 Redis / SQL Server:

// 1. 注册
builder.Services.AddStackExchangeRedisCache(opts =>
{
    opts.Configuration = builder.Configuration["Redis:ConnectionString"];
    opts.InstanceName = "MyApp:";
});

// 2. 用 IDistributedCache
public class ProductService(IDistributedCache cache)
{
    public async Task<List<Product>> GetHotListAsync(CancellationToken ct)
    {
        const string key = "products:hot";
        var cached = await cache.GetAsync(key, ct);
        if (cached is { Length: > 0 })
            return JsonSerializer.Deserialize<List<Product>>(cached)!;

        var fresh = await LoadFromDbAsync(ct);
        await cache.SetAsync(key,
            JsonSerializer.SerializeToUtf8Bytes(fresh),
            new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5) },
            ct);
        return fresh;
    }
}

大文件上传三层方案

ASP.NET Core 大文件上传必须三层同步调,否则只改一处没用:

第一层:Kestrel(应用服务器)

builder.WebHost.ConfigureKestrel(options =>
{
    options.Limits.MaxRequestBodySize = 500L * 1024 * 1024;  // 500 MB
});

第二层:IFormOptions(表单解析)

builder.Services.Configure<FormOptions>(options =>
{
    options.MultipartBodyLengthLimit = 500L * 1024 * 1024;
    options.ValueLengthLimit = 1024 * 1024;
});

第三层:反向代理(IIS / Nginx)

Nginx:

client_max_body_size 500M;

IIS:

<system.webServer>
  <security>
    

Action 级临时覆盖

[HttpPost]
[RequestSizeLimit(500L * 1024 * 1024)]
[RequestFormLimits(MultipartBodyLengthLimit = 500L * 1024 * 1024)]
public async Task<IActionResult> Upload(IFormFile file) { ... }

错误码对照

现象原因检查
请求体被 Kestrel 拒绝MaxRequestBodySize 超限看 Kestrel 日志
请求体被 Form 解析拒绝MultipartBodyLengthLimit 超限看异常类型 InvalidDataException
Nginx 返回 413client_max_body_size看 Nginx access.log
IIS 返回 404.13maxAllowedContentLength前端 body size / 4 KB

完整对照表

.NET Framework 4.xASP.NET Core 8
[OutputCache] 特性[ResponseCache] 特性 + AddResponseCaching
web.config 节点无全局配置,按 Action 装饰
HttpRuntime 静态缓存IMemoryCache / IDistributedCache
OutputCache 依赖 System.Web不依赖,纯中间件
web.config <httpRuntime maxRequestLength>3 处:Kestrel MaxRequestBodySize / FormOptions MultipartBodyLengthLimit / 反代
IIS 默认 30 MB 上限Kestrel 默认 30 MB(仍受 IIS / Nginx 限制)

使用准则

  1. OutputCache / ResponseCache 只缓存"可公开 + 无副作用"响应。用户登录后的页面、CSRF token 等一律不缓存。
  2. 大文件上传永远三层都改——只改 Kestrel 不改 Nginx 一样失败。
  3. 不缓存 GET 之外的方法:POST / PUT / DELETE 都不安全。
  4. VaryByHeader 比 VaryByParam 更精细——按 User-Agent / Accept-Language 区分更合理。
  5. 上传文件大小不是越大越好——大文件吃内存,吃 GC,建议分片上传(tus / ng-file-upload)。
参考资料