IIS 上 ASP.NET Web Forms 的上传大小限制
在 IIS 上,ASP.NET Web Forms 的上传在到达您的代码之前,要先经过两道大小限制。它们使用不同的单位,各自产生不同的错误页面,而且都不是 JSON。本页列出可能拒绝大图片的每一层、示例使用的设置,以及让每次拒绝都以 JSON 返回的两项额外配置(一个 Global.asax 钩子和一行 httpErrors 配置)。第二项只对远程访问者有影响。
各层的顺序
- 图片库对话框。设置
maxUploadFileSize: 4000000后,它会在发送任何内容之前拒绝更大的文件。一个 4,500,008 字节的文件得到了 "big.png is 4.3 MB. The maximum allowed size is 3.8 MB." 的提示。对话框按二进制兆字节计算,并且没有请求到达服务器。这只是为了方便用户,服务器不能依赖它。 - IIS 请求筛选:
maxAllowedContentLength,单位为字节。默认值为 30,000,000。IIS 在 ASP.NET 运行之前根据 Content-Length 进行检查。 - ASP.NET:
<httpRuntime maxRequestLength>,单位为千字节。默认值为 4096,即 4,194,304 字节(在我们的机器上,new HttpRuntimeSection().MaxRequestLength输出了 4096)。它在首次读取请求体时生效。 - 处理程序自己的检查:每个文件 4,000,000 字节,超出时返回 JSON 格式的 413。
<!-- maxRequestLength is in KILOBYTES (default 4096 = 4,194,304 bytes). 5120 KB = 5,242,880
bytes: comfortably above the 4,000,000-byte image limit plus multipart overhead, so a
file that is slightly too big still reaches the handler and gets its JSON 413. -->
<httpRuntime targetFramework="4.8" maxRequestLength="5120" /><requestFiltering>
<!-- maxAllowedContentLength is in BYTES (default 30,000,000). Same 5,242,880 bytes as
maxRequestLength above. IIS checks this one first, before ASP.NET runs. -->
<requestLimits maxAllowedContentLength="5242880" />
</requestFiltering>两者都设置为相同的 5,242,880 字节,一处写成 5120 KB,另一处写成字节数。默认的 4096 KB 其实也能容纳一张 4,000,000 字节的图片加上几百字节的 multipart 开销。留出额外的空间,是为了让稍微超大的文件仍然能到达处理程序,并得到相应的具体消息。
每一层返回什么
| 上传 | 拒绝者 | 没有额外配置时的响应 |
|---|---|---|
| 4,500,008 字节 | 处理程序 | 413 {"error":"The image is larger than the 4 MB (4,000,000 bytes) limit."} |
| 6,000,008 字节 | IIS 请求筛选 | 413.1 "Request Entity Too Large",一个 IIS HTML 页面 |
6,000,008 字节,maxAllowedContentLength 提高到 50,000,000 | ASP.NET | HTML 500 "Maximum request length exceeded." |
较早的指南把 IIS 的拒绝描述为 404.13。IIS 7 和 8 确实使用这个代码,但这里的 IIS Express 10 返回的是 413.1。示例两者都能处理。
把 IIS 页面转换为 JSON
请求筛选在任何处理程序运行之前就拒绝了请求,但托管的 Application_EndRequest 事件仍会为它触发:
// Optional: makes "request too large" from IIS come back as JSON on /rte/ routes.
void Application_EndRequest(object sender, EventArgs e)
{
RteFiles.RewriteTooLarge(Context);
}// Call from Application_EndRequest (Global.asax). A body over maxAllowedContentLength is
// rejected by IIS request filtering before any handler runs - HTTP 413.1 with an HTML
// page (404.13 on IIS 7/8) - but EndRequest still runs, so the /rte/ routes can answer
// in JSON instead. The substatus check leaves the handlers' own 413s alone.
public static void RewriteTooLarge(HttpContext context)
{
HttpResponse response = context.Response;
bool rejectedByIis = (response.StatusCode == 413 && response.SubStatusCode == 1)
|| (response.StatusCode == 404 && response.SubStatusCode == 13);
if (rejectedByIis
&& context.Request.AppRelativeCurrentExecutionFilePath.StartsWith("~/rte/", StringComparison.OrdinalIgnoreCase))
{
WriteError(context, 413, "The upload is larger than the server allows.");
}
}检查子状态码很重要。我们的第一个版本匹配 /rte/ 上的所有 413,结果覆盖了处理程序自己返回的、更精确的 413 消息。
这在本地有效,但对远程访问者仍然失败。IIS Express 和 IIS 默认使用 errorMode="DetailedLocalOnly"。我们设置了 errorMode="Custom",也就是远程访问者得到的模式。结果,在 EndRequest 中写入的 JSON 被替换成了 IIS 的 "The page was not displayed because the request entity is too large."。添加下面的配置块后问题得到解决,契约测试也仍然通过:
<!-- /rte/ always answers in JSON. With errorMode="Custom" (what remote visitors get by
default) IIS would otherwise swap its own HTML page in for the 413 that Global.asax
writes when request filtering rejects an oversized upload. -->
<location path="rte">
<system.webServer>
<httpErrors existingResponse="PassThrough" />
</system.webServer>
</location>ASP.NET 的限制在上传处理程序内部处理。Request.Files 在 try 中读取,带有 WebEventCodes.RuntimeErrorPostTooLarge 的 HttpException 会变成 JSON 413。在提高 IIS 限制、让 ASP.NET 的限制先生效的情况下,一次 6 MB 的上传得到的是这个 JSON,而不是 HTML 500。使用示例自带的设置时,应该只有不带 Content-Length 标头的上传(分块传输)才会走这条路径。我们没有测试这种情况。
测试
在端口 5401 上运行本指南的示例项目(run.ps1),然后执行:
head -c 4500000 /dev/zero > four.jpg # then make it start like a JPEG:
printf '\xff\xd8\xff' | cat - four.jpg > big.jpg
curl -F "file=@big.jpg" http://localhost:5401/rte/upload
{"error":"The image is larger than the 4 MB (4,000,000 bytes) limit."}
head -c 6000000 /dev/urandom > huge.jpg
curl -F "file=@huge.jpg" http://localhost:5401/rte/upload
{"error":"The upload is larger than the server allows."}两者都是带 JSON 的 HTTP 413。第一个来自处理程序,第二个来自 Global.asax 钩子。删除 Global.asax 后再次发送 huge.jpg,就能看到 IIS 的 413.1 页面。
其他平台的同一指南
JavaScript · Next.js · PHP · ASP.NET Core · MVC 5