老代码(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 返回 413 | client_max_body_size | 看 Nginx access.log |
| IIS 返回 404.13 | maxAllowedContentLength | 前端 body size / 4 KB |
完整对照表
| .NET Framework 4.x | ASP.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 限制) |
使用准则
- OutputCache / ResponseCache 只缓存"可公开 + 无副作用"响应。用户登录后的页面、CSRF token 等一律不缓存。
- 大文件上传永远三层都改——只改 Kestrel 不改 Nginx 一样失败。
- 不缓存 GET 之外的方法:POST / PUT / DELETE 都不安全。
VaryByHeader比VaryByParam更精细——按 User-Agent / Accept-Language 区分更合理。- 上传文件大小不是越大越好——大文件吃内存,吃 GC,建议分片上传(tus / ng-file-upload)。