Límites de tamaño de carga en ASP.NET Web Forms sobre IIS
En IIS, una carga de ASP.NET Web Forms pasa por dos límites de tamaño antes de que tu código la vea. Usan unidades distintas, y cada uno produce una página de error distinta, ninguna de ellas en JSON. Esta página enumera todas las capas que pueden rechazar una imagen grande, los ajustes que usa el ejemplo y las dos piezas adicionales (un hook en Global.asax y una línea de httpErrors) que hacen que cada rechazo vuelva como JSON. La segunda pieza solo importa para los visitantes remotos.
Las capas, en orden
- El cuadro de diálogo de la galería. Con
maxUploadFileSize: 4000000, rechaza un archivo más grande antes de enviar nada. Un archivo de 4,500,008 bytes recibió "big.png is 4.3 MB. The maximum allowed size is 3.8 MB." El cuadro de diálogo cuenta en megabytes binarios, y no llegó ninguna solicitud al servidor. Es una comodidad, y el servidor no puede depender de ella. - El filtrado de solicitudes de IIS:
maxAllowedContentLength, en bytes. El valor predeterminado es 30,000,000. IIS lo compara con Content-Length antes de que se ejecute ASP.NET. - ASP.NET:
<httpRuntime maxRequestLength>, en kilobytes. El valor predeterminado es 4096, es decir, 4,194,304 bytes (new HttpRuntimeSection().MaxRequestLengthmostró 4096 en nuestra máquina). Se aplica cuando se lee el cuerpo por primera vez. - La comprobación propia del handler: 4,000,000 bytes por archivo, con una respuesta 413 en JSON.
<!-- 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>Ambos están establecidos en los mismos 5,242,880 bytes, una vez como 5120 KB y otra en bytes. Los 4096 KB predeterminados también darían cabida a una imagen de 4,000,000 bytes más sus pocos cientos de bytes de sobrecarga multipart. El margen extra hace que un archivo un poco demasiado grande siga llegando al handler y reciba su mensaje específico.
Qué devuelve cada capa
| Carga | Rechazada por | Respuesta sin las piezas adicionales |
|---|---|---|
| 4,500,008 bytes | el handler | 413 {"error":"The image is larger than the 4 MB (4,000,000 bytes) limit."} |
| 6,000,008 bytes | el filtrado de solicitudes de IIS | 413.1 "Request Entity Too Large", una página HTML de IIS |
6,000,008 bytes, con maxAllowedContentLength aumentado a 50,000,000 | ASP.NET | HTML 500 "Maximum request length exceeded." |
Las guías más antiguas describen el rechazo de IIS como 404.13. IIS 7 y 8 usaban ese código, pero IIS Express 10 dio aquí 413.1. El ejemplo gestiona ambos.
Convertir la página de IIS en JSON
El filtrado de solicitudes rechaza la solicitud antes de que se ejecute ningún handler, pero el evento administrado Application_EndRequest se sigue disparando para ella:
// 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.");
}
}La comprobación del subestado importa. Nuestra primera versión capturaba todos los 413 de /rte/, y sobrescribía el mensaje 413 propio del handler, más preciso.
Esto funcionaba en local y seguía fallando para los visitantes remotos. IIS Express e IIS usan de forma predeterminada errorMode="DetailedLocalOnly". Establecimos errorMode="Custom", que es lo que recibe un visitante remoto. El JSON escrito en EndRequest se sustituyó entonces por el "The page was not displayed because the request entity is too large." de IIS. Añadir este bloque lo solucionó, y la prueba de contrato siguió pasando:
<!-- /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>El límite de ASP.NET se gestiona en el propio handler de carga. Request.Files se lee dentro de un try, y una HttpException con WebEventCodes.RuntimeErrorPostTooLarge se convierte en un 413 en JSON. Con el límite de IIS aumentado para que se aplicara primero el de ASP.NET, una carga de 6 MB recibió ese JSON en lugar del HTML 500. Con los ajustes incluidos, solo una carga sin encabezado Content-Length (chunked) debería seguir ese camino. No lo probamos.
Pruébalo
Con el proyecto de ejemplo de esta guía en ejecución en el puerto 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."}Ambas son HTTP 413 con JSON. La primera vino del handler y la segunda del hook de Global.asax. Borra Global.asax y vuelve a enviar huge.jpg para ver la página 413.1 de IIS.
La misma guía para otras plataformas
JavaScript · Next.js · PHP · ASP.NET Core · MVC 5