Größenlimits für Uploads in ASP.NET Web Forms unter IIS
Unter IIS durchläuft ein Upload in ASP.NET Web Forms zwei Größenlimits, bevor Ihr Code ihn sieht. Sie verwenden unterschiedliche Einheiten, und jedes erzeugt eine andere Fehlerseite, keine davon JSON. Diese Seite führt jede Ebene auf, die ein großes Bild ablehnen kann, die Einstellungen, die das Beispiel verwendet, und die beiden zusätzlichen Teile (einen Hook in Global.asax und eine Zeile httpErrors), mit denen jede Ablehnung als JSON zurückkommt. Der zweite Teil ist nur für entfernte Besucher wichtig.
Die Ebenen, der Reihe nach
- Der Galerie-Dialog. Mit
maxUploadFileSize: 4000000lehnt er eine größere Datei ab, bevor er etwas sendet. Eine Datei mit 4,500,008 Bytes erhielt „big.png is 4.3 MB. The maximum allowed size is 3.8 MB.“ Der Dialog rechnet in binären Megabytes, und kein Request erreichte den Server. Das ist eine Komfortfunktion, auf die sich der Server nicht verlassen kann. - IIS-Anforderungsfilterung:
maxAllowedContentLength, in Bytes. Der Standardwert ist 30,000,000. IIS prüft ihn gegen Content-Length, bevor ASP.NET läuft. - ASP.NET:
<httpRuntime maxRequestLength>, in Kilobytes. Der Standardwert ist 4096, also 4,194,304 Bytes (new HttpRuntimeSection().MaxRequestLengthgab auf unserem Rechner 4096 aus). Er wird beim ersten Lesen des Bodys durchgesetzt. - Die eigene Prüfung des Handlers: 4,000,000 Bytes pro Datei, beantwortet mit einem 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>Beide sind auf dieselben 5,242,880 Bytes gesetzt, einmal als 5120 KB und einmal in Bytes. Der Standardwert von 4096 KB würde auch für ein Bild mit 4,000,000 Bytes plus die wenigen hundert Bytes Multipart-Overhead reichen. Durch den zusätzlichen Spielraum erreicht eine Datei, die etwas zu groß ist, trotzdem den Handler und erhält dessen spezifische Meldung.
Was jede Ebene zurücksendet
| Upload | Abgelehnt von | Antwort ohne die zusätzlichen Teile |
|---|---|---|
| 4,500,008 Bytes | dem Handler | 413 {"error":"The image is larger than the 4 MB (4,000,000 bytes) limit."} |
| 6,000,008 Bytes | der IIS-Anforderungsfilterung | 413.1 „Request Entity Too Large“, eine HTML-Seite von IIS |
6,000,008 Bytes, mit maxAllowedContentLength auf 50,000,000 erhöht | ASP.NET | HTML-Seite mit 500 „Maximum request length exceeded.“ |
Ältere Anleitungen beschreiben die Ablehnung durch IIS als 404.13. IIS 7 und 8 verwendeten diesen Code, IIS Express 10 gab hier aber 413.1 zurück. Das Beispiel behandelt beides.
Die IIS-Seite in JSON umwandeln
Die Anforderungsfilterung lehnt den Request ab, bevor ein Handler läuft, aber das verwaltete Ereignis Application_EndRequest wird dafür trotzdem ausgelöst:
// 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.");
}
}Die Prüfung des Unterstatus ist wichtig. Unsere erste Version erfasste jedes 413 unter /rte/ und überschrieb dabei die eigene, genauere 413-Meldung des Handlers.
Lokal funktionierte das, für entfernte Besucher schlug es trotzdem fehl. IIS Express und IIS verwenden standardmäßig errorMode="DetailedLocalOnly". Wir haben errorMode="Custom" gesetzt, was ein entfernter Besucher erhält. Das in EndRequest geschriebene JSON wurde dann durch die IIS-Meldung „The page was not displayed because the request entity is too large.“ ersetzt. Dieser Block behob das, und der Vertragstest bestand weiterhin:
<!-- /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>Das Limit von ASP.NET wird im Upload-Handler selbst behandelt. Request.Files wird innerhalb eines try gelesen, und eine HttpException mit WebEventCodes.RuntimeErrorPostTooLarge wird zu einem JSON-413. Mit erhöhtem IIS-Limit, sodass das Limit von ASP.NET zuerst griff, erhielt ein Upload mit 6 MB dieses JSON statt der HTML-Seite mit 500. Mit den ausgelieferten Einstellungen sollte nur ein Upload ohne Header Content-Length (Chunked) diesen Weg nehmen. Einen solchen haben wir nicht getestet.
Testen
Während das Beispielprojekt zu dieser Anleitung auf Port 5401 läuft (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."}Beide sind HTTP 413 mit JSON. Das erste kam vom Handler, das zweite vom Hook in Global.asax. Löschen Sie Global.asax und senden Sie huge.jpg erneut, um die Seite 413.1 von IIS zu sehen.
Dieselbe Anleitung für andere Plattformen
JavaScript · Next.js · PHP · ASP.NET Core · MVC 5