Łapanie „Przekroczono maksymalną długość żądania”

112

Piszę funkcję wysyłania i mam problemy z wychwyceniem „System.Web.HttpException: Przekroczono maksymalną długość żądania” w przypadku plików większych niż określony maksymalny rozmiar httpRuntimew web.config (maksymalny rozmiar ustawiony na 5120). Używam prostego <input>pliku.

Problem polega na tym, że wyjątek jest zgłaszany przed zdarzeniem kliknięcia przycisku przesyłania, a wyjątek występuje przed uruchomieniem mojego kodu. Jak więc złapać i obsłużyć wyjątek?

EDYCJA: Wyjątek jest rzucany natychmiast, więc jestem prawie pewien, że nie jest to problem z limitem czasu z powodu wolnych połączeń.

Marcus L.
źródło
5
Czy ktoś próbował tego z MVC? Wydaje się, że jestem w stanie złapać wyjątek we właściwy sposób, ale nie mogę go zatrzymać: za każdym razem, gdy próbuję renderować stronę błędu, występuje ten sam wyjątek.
Andy,
Ten komunikat o błędzie jest generowany przez usługi IIS przed dotarciem do kontrolera. Aby powiadomić użytkownika, że ​​plik przekracza maksymalny limit wysyłania (ustawiony w Twojej konfiguracji internetowej), możesz bezpośrednio zweryfikować rozmiar pliku za pomocą JS za pomocą zdarzenia onchange. Na przykład <input type="file" id="upload" name="upload" onchange="showFileSize();" />Inside showFileSize(), możesz wyświetlić komunikat o błędzie w oparciu o rozmiar pliku za pośrednictwem var input = document.getElementById("upload"); var file = input.files[0];i dołączyć znacznik HTML.
Alfred Wallace

Odpowiedzi:

97

Niestety nie ma łatwego sposobu na złapanie takiego wyjątku. To, co robię, to albo nadpisuję metodę OnError na poziomie strony, albo Application_Error w global.asax, a następnie sprawdzam, czy była to awaria maksymalnego żądania, a jeśli tak, przenoszę na stronę błędu.

protected override void OnError(EventArgs e) .....


private void Application_Error(object sender, EventArgs e)
{
    if (GlobalHelper.IsMaxRequestExceededException(this.Server.GetLastError()))
    {
        this.Server.ClearError();
        this.Server.Transfer("~/error/UploadTooLarge.aspx");
    }
}

To hack, ale poniższy kod działa dla mnie

const int TimedOutExceptionCode = -2147467259;
public static bool IsMaxRequestExceededException(Exception e)
{
    // unhandled errors = caught at global.ascx level
    // http exception = caught at page level

    Exception main;
    var unhandled = e as HttpUnhandledException;

    if (unhandled != null && unhandled.ErrorCode == TimedOutExceptionCode)
    {
        main = unhandled.InnerException;
    }
    else
    {
        main = e;
    }


    var http = main as HttpException;

    if (http != null && http.ErrorCode == TimedOutExceptionCode)
    {
        // hack: no real method of identifying if the error is max request exceeded as 
        // it is treated as a timeout exception
        if (http.StackTrace.Contains("GetEntireRawContent"))
        {
            // MAX REQUEST HAS BEEN EXCEEDED
            return true;
        }
    }

    return false;
}
Damien McGivern
źródło
2
Dzięki. OnError nie działał, ale Application_Error tak. Właściwie mamy do tego program obsługi, ale ktoś wyłączył go w kodzie.
Marcus L
jeszcze dwa lata temu, ale nadal chcę zapytać, czy do tej pory działało to dobrze? czy porównanie ciągów „GetEntireRawContent” działa dobrze? Nie sądzę, że jest to problem z przekroczeniem limitu czasu. Czy jest ktoś wyróżniający się, wskazujący mi gdzieś na niepogodę w tej sprawie?
Elaine
@Elaine yeah, ta technika nadal działa z ASP.Net 4.0. W przypadku próby przekazania żądania, które jest większe niż maksymalna długość żądania, ASP.Net zgłasza HttpException z kodem limitu czasu. Zajrzyj do System.Web.HttpRequest.GetEntireRawContent () przy użyciu reflektora.
Damien McGivern
12
@ sam-rueby Nie chciałem odpowiadać na komunikat o błędzie w postaci ciągu znaków, który może ulec zmianie z powodu lokalizacji.
Damien McGivern
4
W przypadku platformy .NET 4.0 i nowszych jest lepszy sposób na określenie, czy przekroczono maksymalny rozmiar żądania. Możesz sprawdzić ten warunek dla HttpException: httpException.WebEventCode == WebEventCodes.RuntimeErrorPostTooLarge- używając System.Web.Management.WebEventCodes
mco
58

Jak powiedział GateKiller, musisz zmienić maxRequestLength. Może być także konieczna zmiana ExecutionTimeout w przypadku, gdy prędkość wysyłania jest zbyt niska. Pamiętaj, że nie chcesz, aby którekolwiek z tych ustawień było zbyt duże, w przeciwnym razie będziesz otwarty na ataki DOS.

Wartość domyślna dla ExecutionTimeout to 360 sekund lub 6 minut.

Możesz zmienić maxRequestLength i ExecutionTimeout za pomocą elementu httpRuntime .

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <system.web>
        <httpRuntime maxRequestLength="102400" executionTimeout="1200" />
    </system.web>
</configuration>

EDYTOWAĆ:

Jeśli chcesz obsłużyć wyjątek niezależnie od tego, jak już wspomniano, musisz to zrobić w Global.asax. Oto link do przykładu kodu .

Jonathan Parker
źródło
2
Dziękuję za odpowiedź, ale jak powiedziałem w moim komentarzu do odpowiedzi GK, to tak naprawdę nie rozwiązuje mojego problemu. Nie jest to również problem z limitem czasu, ponieważ wyjątek jest zgłaszany natychmiast. Zmienię pytanie, aby było jaśniejsze.
Marcus L
4
Przykładowy adres URL w kodzie wskazuje na niedostępną stronę ... czy ktoś może to naprawić?
deostroll
20

Możesz rozwiązać ten problem, zwiększając maksymalną długość żądania w pliku web.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <system.web>
        <httpRuntime maxRequestLength="102400" />
    </system.web>
</configuration>

Powyższy przykład dotyczy limitu 100 Mb.

GateKiller
źródło
18
Tak i nie. Przesuwasz limit dalej, ale tak naprawdę nie obsługuje wyjątku. Nadal będziesz mieć ten sam problem, jeśli ktoś spróbuje przesłać 101+ Mb. Limit naprawdę musi wynosić 5 Mb.
Marcus L
10

Jeśli chcesz również walidacji po stronie klienta, aby zmniejszyć potrzebę zgłaszania wyjątków, możesz spróbować zaimplementować walidację rozmiaru pliku po stronie klienta.

Uwaga: działa to tylko w przeglądarkach obsługujących HTML5. http://www.html5rocks.com/en/tutorials/file/dndfiles/

<form id="FormID" action="post" name="FormID">
    <input id="target" name="target" class="target" type="file" />
</form>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" type="text/javascript"></script>

<script type="text/javascript" language="javascript">

    $('.target').change(function () {

        if (typeof FileReader !== "undefined") {
            var size = document.getElementById('target').files[0].size;
            // check file size

            if (size > 100000) {

                $(this).val("");

            }
        }

    });

</script>

Andrzej
źródło
9

Witam rozwiązanie wspomniane przez Damiena McGivern, działa tylko na IIS6,

Nie działa w usługach IIS7 i ASP.NET Development Server. Na stronie pojawia się komunikat „404 - Nie znaleziono pliku lub katalogu”.

Jakieś pomysły?

EDYTOWAĆ:

Rozumiem ... To rozwiązanie nadal nie działa na serwerze deweloperskim ASP.NET, ale mam powód, dla którego w moim przypadku nie działa na IIS7.

Powodem jest to, że IIS7 ma wbudowane skanowanie żądań, które narzuca limit pliku do przesłania, który domyślnie wynosi 30000000 bajtów (czyli nieco mniej niż 30 MB).

Próbowałem wgrać plik o rozmiarze 100 MB, aby przetestować rozwiązanie wspomniane przez Damiena McGivern (z maxRequestLength = "10240" czyli 10MB w web.config). Teraz, jeśli prześlę plik o rozmiarze> 10 MB i <30 MB, strona zostanie przekierowana na określoną stronę błędu. Ale jeśli rozmiar pliku wynosi> 30 MB, zostanie wyświetlona brzydka wbudowana strona błędu wyświetlająca „404 - Nie znaleziono pliku lub katalogu”.

Aby tego uniknąć, musisz zwiększyć maks. dozwolona długość treści żądania dla Twojej witryny w usługach IIS7. Można to zrobić za pomocą następującego polecenia,

appcmd set config "SiteName" -section:requestFiltering -requestLimits.maxAllowedContentLength:209715200 -commitpath:apphost

Ustawiłem max. długość zawartości do 200 MB.

Po wykonaniu tego ustawienia strona jest pomyślnie przekierowywana na moją stronę błędu, gdy próbuję przesłać plik o wielkości 100 MB

Więcej informacji można znaleźć pod adresem http://weblogs.asp.net/jgalloway/archive/2008/01/08/large-file-uploads-in-asp-net.aspx .

Vinod T. Patil
źródło
Przepraszam! Dodałem moje zapytanie jako odpowiedź, nie wiem, jak dodawać komentarze do istniejących postów.
Vinod T. Patil
1
Po prostu potrzebujesz więcej rep. komentować posty. Zobacz często zadawane pytania, aby uzyskać więcej informacji o tym, co możesz, a czego nie możesz zrobić ze swoim obecnym przedstawicielem.
slaphappy
7

Oto alternatywny sposób, który nie obejmuje żadnych „hacków”, ale wymaga ASP.NET 4.0 lub nowszego:

//Global.asax
private void Application_Error(object sender, EventArgs e)
{
    var ex = Server.GetLastError();
    var httpException = ex as HttpException ?? ex.InnerException as HttpException;
    if(httpException == null) return;

    if(httpException.WebEventCode == WebEventCodes.RuntimeErrorPostTooLarge)
    {
        //handle the error
        Response.Write("Sorry, file is too big"); //show this message for instance
    }
}
Serge Shultz
źródło
4

Jednym ze sposobów jest ustawienie maksymalnego rozmiaru w pliku web.config, jak już wspomniano powyżej, np

<system.web>         
    <httpRuntime maxRequestLength="102400" />     
</system.web>

wtedy kiedy obsłużysz zdarzenie uploadu, sprawdź rozmiar i jeśli przekracza określoną kwotę, możesz go złapać np

protected void btnUploadImage_OnClick(object sender, EventArgs e)
{
    if (fil.FileBytes.Length > 51200)
    {
         TextBoxMsg.Text = "file size must be less than 50KB";
    }
}
BaggieBoy
źródło
1
To nie działa. Zdarzenie kliknięcia nigdy nie jest wyzwalane. Wyjątek zdarza się wcześniej.
Lombas
3

W usługach IIS 7 i nowszych:

plik web.config:

<system.webServer>
  <security >
    <requestFiltering>
      <requestLimits maxAllowedContentLength="[Size In Bytes]" />
    </requestFiltering>
  </security>
</system.webServer>

Następnie możesz sprawdzić kod za, na przykład:

If FileUpload1.PostedFile.ContentLength > 2097152 Then ' (2097152 = 2 Mb)
  ' Exceeded the 2 Mb limit
  ' Do something
End If

Po prostu upewnij się, że [Rozmiar w bajtach] w pliku web.config jest większy niż rozmiar pliku, który chcesz przesłać, wtedy błąd 404 nie zostanie wyświetlony. Następnie możesz sprawdzić rozmiar pliku w kodzie za pomocą ContentLength, co byłoby znacznie lepsze

Walery Strauch
źródło
2

Jak zapewne wiesz, maksymalna długość żądania jest skonfigurowana w DWÓCH miejscach.

  1. maxRequestLength - kontrolowane z poziomu aplikacji ASP.NET
  2. maxAllowedContentLength - pod <system.webServer> kontrolą na poziomie IIS

Pierwszy przypadek jest objęty innymi odpowiedziami na to pytanie.

Aby złapać DRUGI , musisz to zrobić w global.asax:

protected void Application_EndRequest(object sender, EventArgs e)
{
    //check for the "file is too big" exception if thrown at the IIS level
    if (Response.StatusCode == 404 && Response.SubStatusCode == 13)
    {
        Response.Write("Too big a file"); //just an example
        Response.End();
    }
}
jazzcat
źródło
1

Po tagu

<security>
     <requestFiltering>
         <requestLimits maxAllowedContentLength="4500000" />
     </requestFiltering>
</security>

dodaj następujący tag

 <httpErrors errorMode="Custom" existingResponse="Replace">
  <remove statusCode="404" subStatusCode="13" />
  <error statusCode="404" subStatusCode="13" prefixLanguageFilePath="" path="http://localhost/ErrorPage.aspx" responseMode="Redirect" />
</httpErrors>

możesz dodać adres URL do strony błędu ...

Diego
źródło
0

Możesz rozwiązać ten problem, zwiększając maksymalną długość żądania i limit czasu wykonania w pliku web.config:

-Proszę wyjaśnić maksymalny czas wykonania, który upłynął później niż 1200

<?xml version="1.0" encoding="utf-8"?> <configuration> <system.web> <httpRuntime maxRequestLength="102400" executionTimeout="1200" /> </system.web> </configuration>
Nasurudeen
źródło
0

Co powiesz na złapanie go na imprezie EndRequest?

protected void Application_EndRequest(object sender, EventArgs e)
    {
        HttpRequest request = HttpContext.Current.Request;
        HttpResponse response = HttpContext.Current.Response;
        if ((request.HttpMethod == "POST") &&
            (response.StatusCode == 404 && response.SubStatusCode == 13))
        {
            // Clear the response header but do not clear errors and
            // transfer back to requesting page to handle error
            response.ClearHeaders();
            HttpContext.Current.Server.Transfer(request.AppRelativeCurrentExecutionFilePath);
        }
    }
Khoa Tran
źródło
0

Można to sprawdzić poprzez:

        var httpException = ex as HttpException;
        if (httpException != null)
        {
            if (httpException.WebEventCode == System.Web.Management.WebEventCodes.RuntimeErrorPostTooLarge)
            {
                // Request too large

                return;

            }
        }
Raghav
źródło