Limites de taille d’envoi avec ASP.NET Web Forms sous IIS
Sous IIS, un envoi ASP.NET Web Forms franchit deux limites de taille avant que votre code ne le voie. Elles utilisent des unités différentes, et chacune produit une page d’erreur différente, aucune des deux en JSON. Cette page liste chaque couche qui peut refuser une grosse image, les réglages qu’utilise l’exemple, et les deux éléments supplémentaires (un hook dans Global.asax et une ligne httpErrors) qui font revenir chaque refus en JSON. Le second élément ne compte que pour les visiteurs distants.
Les couches, dans l’ordre
- La boîte de dialogue de la galerie. Avec
maxUploadFileSize: 4000000, elle refuse un fichier plus grand avant tout envoi. Un fichier de 4,500,008 octets a reçu « big.png is 4.3 MB. The maximum allowed size is 3.8 MB. ». La boîte de dialogue compte en mégaoctets binaires, et aucune requête n’a atteint le serveur. C’est une commodité, et le serveur ne peut pas s’y fier. - Le filtrage des requêtes d’IIS :
maxAllowedContentLength, en octets. La valeur par défaut est 30,000,000. IIS la compare à Content-Length avant l’exécution d’ASP.NET. - ASP.NET :
<httpRuntime maxRequestLength>, en kilo-octets. La valeur par défaut est 4096, soit 4,194,304 octets (new HttpRuntimeSection().MaxRequestLengtha affiché 4096 sur notre machine). Elle est appliquée à la première lecture du corps. - La vérification propre au gestionnaire : 4,000,000 octets par fichier, avec une réponse 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>Les deux sont réglées sur les mêmes 5,242,880 octets, une fois en 5120 Ko et une fois en octets. La valeur par défaut de 4096 Ko suffirait aussi pour une image de 4,000,000 octets plus ses quelques centaines d’octets de surcoût multipart. La marge supplémentaire permet à un fichier légèrement trop gros d’atteindre quand même le gestionnaire et de recevoir son message spécifique.
Ce que renvoie chaque couche
| Envoi | Refusé par | Réponse sans les éléments supplémentaires |
|---|---|---|
| 4,500,008 octets | le gestionnaire | 413 {"error":"The image is larger than the 4 MB (4,000,000 bytes) limit."} |
| 6,000,008 octets | le filtrage des requêtes d’IIS | 413.1 « Request Entity Too Large », une page HTML d’IIS |
6,000,008 octets, avec maxAllowedContentLength relevé à 50,000,000 | ASP.NET | HTML 500 « Maximum request length exceeded. » |
Les guides plus anciens décrivent le refus d’IIS comme un 404.13. IIS 7 et 8 utilisaient ce code, mais IIS Express 10 a donné ici un 413.1. L’exemple gère les deux.
Transformer la page d’IIS en JSON
Le filtrage des requêtes rejette la requête avant l’exécution de tout gestionnaire, mais l’événement managé Application_EndRequest se déclenche quand même :
// 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 vérification du sous-statut compte. Notre première version interceptait tous les 413 sur /rte/, et elle écrasait le message 413 propre au gestionnaire, plus précis.
Cela fonctionnait en local et échouait encore pour les visiteurs distants. IIS Express et IIS utilisent par défaut errorMode="DetailedLocalOnly". Nous avons défini errorMode="Custom", ce que reçoit un visiteur distant. Le JSON écrit dans EndRequest a alors été remplacé par « The page was not displayed because the request entity is too large. » d’IIS. L’ajout de ce bloc a corrigé le problème, et le test de contrat passait toujours :
<!-- /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>La limite d’ASP.NET est gérée dans le gestionnaire d’envoi lui-même. Request.Files est lu dans un try, et une HttpException avec WebEventCodes.RuntimeErrorPostTooLarge devient un 413 JSON. Avec la limite d’IIS relevée pour que celle d’ASP.NET s’applique en premier, un envoi de 6 Mo a reçu ce JSON au lieu de l’erreur HTML 500. Avec les réglages fournis, seul un envoi sans en-tête Content-Length (chunked) devrait emprunter ce chemin. Nous n’en avons pas testé.
Tester
Avec le projet d’exemple de ce guide en cours d’exécution sur le port 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."}Les deux réponses sont des HTTP 413 en JSON. La première vient du gestionnaire et la seconde du hook de Global.asax. Supprimez Global.asax et envoyez de nouveau huge.jpg pour voir la page 413.1 d’IIS.
Le même guide pour d’autres plateformes
JavaScript · Next.js · PHP · ASP.NET Core · MVC 5