Limites de taille d’envoi avec ASP.NET Core : Kestrel, IIS et MVC
Avec ASP.NET Core, une grande photo peut être refusée à six endroits, et seul le dernier est votre code. Les autres répondent à leur manière : un 413 sans corps, un ProblemDetails 400 de MVC, une page d’erreur IIS, ou une connexion coupée que le navigateur signale comme une erreur réseau. Cette page liste les couches dans l’ordre où une requête les rencontre, avec ce que chacune a renvoyé lors de nos tests.
Les couches, dans l’ordre
- L’éditeur. Avec
maxUploadFileSize: 4000000dans les options, la galerie refuse un fichier plus grand avant de l’envoyer. Un fichier de 4.5 Mo a donné « big.jpg is 4.3 MB. The maximum allowed size is 3.8 MB. ». La boîte de dialogue compte en mégaoctets binaires. Le serveur ne peut pas s’appuyer sur cette vérification. - IIS, si l’application s’exécute in-process derrière lui. D’après la documentation de Microsoft, le filtrage des requêtes rejette les corps qui dépassent
maxAllowedContentLength(en octets, 30,000,000 par défaut) avec la page 404.13 propre à IIS, avant qu’ASP.NET Core ne s’exécute. Nous n’avons pas testé IIS pour cette page. - Le
MaxRequestBodySizede Kestrel, 30,000,000 octets par défaut. Un envoi de 31,000,000 octets a reçu un 413. - La limite propre à la route.
[RequestSizeLimit]remplace la limite de Kestrel pour une action. Dans une Minimal API, le même attribut fonctionne comme métadonnée du point de terminaison. - L’analyseur multipart.
FormOptions.MultipartBodyLengthLimitvaut 134,217,728 octets par défaut. Définissez-le par action avec[RequestFormLimits], ou par point de terminaison avec.WithFormOptions. - Votre vérification : 4,000,000 octets dans l’exemple, avec une réponse 413 en JSON.
Le code
public const long MaxFileBytes = 4_000_000;
// The request is a little bigger than the file: multipart boundaries, headers, the "folder" field.
public const long MaxRequestBytes = MaxFileBytes + 64 * 1024;La limite de requête est la limite de fichier plus 64 Ko pour les délimiteurs multipart et le champ folder. Cet écart est voulu : un fichier qui dépasse à peine 4,000,000 octets atteint quand même le code et reçoit une réponse lisible.
// POST /rte/upload file, folder
[HttpPost("upload")]
[RequestSizeLimit(RteFiles.MaxRequestBytes)] // the whole request body
[RequestFormLimits(MultipartBodyLengthLimit = RteFiles.MaxRequestBytes)] // the multipart parser
public async Task<IActionResult> Upload(IFormFile? file, [FromForm] string? folder) =>
Send(await RteFiles.Save(HttpContext, file, folder));rte.MapPost("/upload", async (HttpContext http, IFormFile? file, [FromForm] string? folder) =>
Json(await RteFiles.Save(http, file, folder)))
.DisableAntiforgery()
// Kestrel accepts 30,000,000-byte bodies by default; this route needs ~4 MB.
.WithMetadata(new RequestSizeLimitAttribute(RteFiles.MaxRequestBytes))
.WithFormOptions(multipartBodyLengthLimit: RteFiles.MaxRequestBytes);Ce que voit le navigateur
Quand une requête dépasse la limite de la route, Kestrel répond 413 et ferme la connexion pendant que le navigateur est encore en train d’envoyer. Nous avons envoyé 10 Mo depuis Chromium avec une XHR, en contournant la vérification propre à l’éditeur, dans les quatre exemples ASP.NET Core. À chaque fois, la XHR s’est terminée dans onerror avec le statut 0, jamais avec un 413. rte-files.js signale alors « The server could not be reached. ». curl, qui attend la réponse, a bien reçu le 413. C’est donc le maxUploadFileSize de l’éditeur qui donne aux utilisateurs un message utile pour les gros fichiers. Gardez-le égal à la limite du serveur.
MVC répond 400, pas 413
Dans une Minimal API, un corps qui dépasse la limite de la route échoue pendant la liaison des paramètres, et le point de terminaison répond 413 avec un corps vide. Dans un [ApiController], l’échec a lieu pendant la liaison du modèle, et la validation automatique du modèle répond par un ProblemDetails 400 :
{"type":"https://tools.ietf.org/html/rfc9110#section-15.5.1","title":"One or more validation errors occurred.","status":400,
"errors":{"":["Failed to read the request form. Request body too large. The max request body size is 4065536 bytes."]}}Un fichier de 4,100,000 octets a donné la même réponse, car avec l’enveloppe multipart la requête dépassait tout juste 4,065,536 octets. Le 413 {"error": ...} de la vérification propre à l’exemple exige que toute la requête tienne sous la limite de la route, ce qui est le cas d’un fichier de 4,050,000 octets.
Au-delà de MultipartBodyLengthLimit, avec la limite de requête désactivée, un point de terminaison Minimal API a lui aussi répondu 400 (envoi de 135,000,000 octets face à la valeur par défaut de 134,217,728).
Relever la limite
Modifiez ensemble MaxFileBytes dans RteFiles.cs et maxUploadFileSize dans la page. MaxRequestBytes suit MaxFileBytes : la limite de la route et la limite multipart évoluent donc avec elle. Au-delà de 30,000,000 octets, chaque couche placée en amont doit aussi l’autoriser : la limite globale de Kestrel s’applique toujours aux autres routes, et la documentation de Microsoft indique qu’IIS nécessite de relever maxAllowedContentLength dans web.config, car [RequestSizeLimit] ne peut pas la dépasser.
Tester
Lancez le projet d’exemple de ce guide avec dotnet run (port 5302), et l'exemple Minimal API (port 5301) à côté :
head -c 4050000 /dev/zero > big.jpg
curl -F "file=@big.jpg" http://localhost:5302/rte/upload
{"error":"The image is larger than 4 MB."}
head -c 10000000 /dev/zero > huge.jpg
curl -s -w "\nHTTP %{http_code}\n" -F "file=@huge.jpg" http://localhost:5301/rte/upload
HTTP 413La première réponse est un 413 de la vérification de l’exemple, sur l’un ou l’autre port. La seconde est le 413 de Kestrel, sans corps. Envoyez huge.jpg au port 5302 pour voir le 400 de MVC décrit plus haut.
Le même guide pour d’autres plateformes
JavaScript · Next.js · PHP · MVC 5 · Web Forms