Limites de tamanho de upload no ASP.NET Core: Kestrel, IIS e MVC
Uma foto grande pode ser barrada em seis lugares no ASP.NET Core, e só o último é o seu código. Os outros respondem cada um à sua maneira: um 413 sem corpo, um ProblemDetails 400 do MVC, uma página de erro do IIS ou uma conexão encerrada que o navegador relata como erro de rede. Esta página lista as camadas na ordem em que uma requisição passa por elas, com o que cada uma devolveu quando testamos.
As camadas, em ordem
- O editor. Com
maxUploadFileSize: 4000000nas opções, a galeria recusa um arquivo maior antes de enviá-lo. Um arquivo de 4.5 MB gerou "big.jpg is 4.3 MB. The maximum allowed size is 3.8 MB." A caixa de diálogo conta em megabytes binários. O servidor não pode depender disso. - IIS, se o aplicativo roda in-process atrás dele. Segundo a documentação da Microsoft, a filtragem de requisições rejeita corpos acima de
maxAllowedContentLength(em bytes, 30,000,000 por padrão) com a página 404.13 do próprio IIS, antes de o ASP.NET Core rodar. Não testamos o IIS para esta página. MaxRequestBodySizedo Kestrel, 30,000,000 bytes por padrão. Um upload de 31,000,000 bytes recebeu 413.- O limite da própria rota.
[RequestSizeLimit]substitui o limite do Kestrel para uma action. Em uma Minimal API, o mesmo atributo funciona como metadado do endpoint. - O parser de multipart.
FormOptions.MultipartBodyLengthLimité 134,217,728 bytes por padrão. Defina-o por action com[RequestFormLimits]ou por endpoint com.WithFormOptions. - A sua verificação: 4,000,000 bytes no exemplo, respondida com um 413 em JSON.
O código
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;O limite da requisição é o limite do arquivo mais 64 KB para os delimitadores do multipart e o campo folder. Essa folga é proposital: um arquivo um pouco acima de 4,000,000 bytes ainda chega ao código e recebe uma resposta legível.
// 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);O que o navegador vê
Quando uma requisição passa do limite da rota, o Kestrel responde 413 e fecha a conexão enquanto o navegador ainda está enviando. Enviamos 10 MB a partir do Chromium com uma XHR, contornando a verificação do próprio editor, nos quatro exemplos ASP.NET Core. Em todas as vezes, a XHR terminou em onerror com status 0, nunca com um 413. O rte-files.js então informa "The server could not be reached." O curl, que espera pela resposta, viu o 413. Portanto, é o maxUploadFileSize do editor que dá aos usuários uma mensagem útil para arquivos grandes. Mantenha-o igual ao limite do servidor.
O MVC responde 400, não 413
Em uma Minimal API, um corpo acima do limite da rota falha durante o binding de parâmetros, e o endpoint responde 413 com o corpo vazio. Em um [ApiController], a falha acontece durante o model binding, e a validação automática do modelo responde com um 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."]}}Um arquivo de 4,100,000 bytes gerou a mesma resposta, porque, com o enquadramento do multipart, a requisição ficou um pouco acima de 4,065,536 bytes. O 413 com {"error": ...} da verificação do próprio exemplo exige que a requisição inteira caiba no limite da rota, o que acontece com um arquivo de 4,050,000 bytes.
Acima de MultipartBodyLengthLimit, com o limite da requisição desativado, um endpoint de Minimal API também respondeu 400 (upload de 135,000,000 bytes contra o padrão de 134,217,728).
Aumentando o limite
Altere MaxFileBytes em RteFiles.cs e maxUploadFileSize na página ao mesmo tempo. MaxRequestBytes acompanha MaxFileBytes, então o limite da rota e o limite do multipart mudam junto. Acima de 30,000,000 bytes, todas as camadas à frente também precisam permitir: o limite global do Kestrel continua valendo para as outras rotas, e a documentação da Microsoft diz que o IIS precisa que maxAllowedContentLength seja aumentado em web.config, porque [RequestSizeLimit] não pode ultrapassá-lo.
Teste
Execute o projeto de exemplo deste guia com dotnet run (porta 5302) e, ao lado dele, o exemplo de Minimal API (porta 5301):
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 413A primeira resposta é um 413 da verificação do exemplo, em qualquer uma das portas. A segunda é o 413 do Kestrel, sem corpo. Envie huge.jpg para a porta 5302 para ver o 400 do MVC mostrado acima.
O mesmo guia para outras plataformas
JavaScript · Next.js · PHP · MVC 5 · Web Forms