Detectando "Se excedió la longitud máxima de la solicitud"

112

Estoy escribiendo una función de carga y tengo problemas para detectar "System.Web.HttpException: Excedió la longitud máxima de la solicitud" con archivos más grandes que el tamaño máximo especificado en httpRuntime en web.config (tamaño máximo establecido en 5120). Estoy usando un simple <input>para el archivo.

El problema es que la excepción se lanza antes del evento de clic del botón de carga, y la excepción ocurre antes de que se ejecute mi código. Entonces, ¿cómo atrapo y manejo la excepción?

EDITAR: La excepción se lanza instantáneamente, así que estoy bastante seguro de que no es un problema de tiempo de espera debido a conexiones lentas.

Marcus L
fuente
5
¿Alguien intentó esto con MVC? Parece que puedo detectar la excepción de la manera correcta, pero no puedo detenerla: cada vez que intento generar una página de error, se produce la misma excepción.
Andy
IIS lanza este mensaje de error antes de llegar al controlador. Para notificar al usuario que el archivo excede el límite máximo de carga (establecido en su configuración web), puede validar directamente el tamaño del archivo a través de JS con un evento onchange. Por ejemplo , <input type="file" id="upload" name="upload" onchange="showFileSize();" />Inside showFileSize(), puede mostrar un mensaje de error basado en el tamaño de su archivo var input = document.getElementById("upload"); var file = input.files[0];y agregar una etiqueta html.
Alfred Wallace

Respuestas:

97

Desafortunadamente, no existe una manera fácil de detectar esta excepción. Lo que hago es anular el método OnError en el nivel de la página o Application_Error en global.asax, luego verificar si fue una falla de Max Request y, si es así, transferirlo a una página de error.

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");
    }
}

Es un truco, pero el siguiente código funciona para mí.

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
fuente
2
Gracias. OnError no funcionó, pero Application_Error sí. De hecho, tenemos un controlador para esto, pero alguien lo había desactivado en el código.
Marcus L
incluso hace dos años, pero todavía quiero preguntar si esto funcionó bien hasta ahora. ¿La comparación de cadenas de 'GetEntireRawContent' funciona bien? No creo que esto sea un problema de tiempo de espera. ¿Hay alguien que se destaque por señalarme un lugar despejado con respecto a esto?
Elaine
@Elaine sí, esta técnica todavía funciona con ASP.Net 4.0. Si intenta cargar una solicitud que es mayor que la longitud máxima de la solicitud, ASP.Net arroja una HttpException con el código de tiempo de espera. Eche un vistazo en System.Web.HttpRequest.GetEntireRawContent () usando reflector.
Damien McGivern
12
@ sam-rueby No quería responder a un mensaje de error de cadena que podría cambiar debido a la localización.
Damien McGivern
4
Para .NET 4.0 y versiones posteriores, existe una mejor manera de identificar si se excedió el tamaño máximo de solicitud. Puede verificar esta condición para HttpException: httpException.WebEventCode == WebEventCodes.RuntimeErrorPostTooLarge- usando System.Web.Management.WebEventCodes
mco
58

Como dijo GateKiller, debe cambiar maxRequestLength. También es posible que deba cambiar el executionTimeout en caso de que la velocidad de carga sea demasiado lenta. Tenga en cuenta que no desea que ninguna de estas configuraciones sea demasiado grande, de lo contrario estará expuesto a ataques de DOS.

El valor predeterminado para el tiempo de espera de ejecución es 360 segundos o 6 minutos.

Puede cambiar maxRequestLength y executionTimeout con el elemento httpRuntime .

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

EDITAR:

Si desea manejar la excepción independientemente, como ya se ha dicho, deberá manejarla en Global.asax. Aquí hay un enlace a un ejemplo de código .

Jonathan Parker
fuente
2
Gracias por su respuesta, pero como dije en mi comentario a la respuesta de GK, esto realmente no resuelve mi problema. Tampoco es un problema de tiempo de espera, ya que la excepción se lanza instantáneamente. Editaré la pregunta para que quede más claro.
Marcus L
4
La URL de ejemplo de código apunta a una página que no está disponible ... ¿alguien puede arreglar esto?
deostroll
20

Puede resolver esto aumentando la longitud máxima de la solicitud en su web.config:

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

El ejemplo anterior es para un límite de 100 Mb.

GateKiller
fuente
18
Si y no. Empujas el límite más, pero realmente no maneja la excepción. Seguirás teniendo el mismo problema si alguien intenta cargar más de 101 Mb. El límite realmente debe ser de 5 Mb.
Marcus L
10

Si también desea una validación del lado del cliente para que tenga menos necesidad de lanzar excepciones, puede intentar implementar la validación del tamaño del archivo del lado del cliente.

Nota: esto solo funciona en navegadores compatibles con 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>

Andrés
fuente
9

Hola solución mencionada por Damien McGivern, funciona solo en IIS6,

No funciona en IIS7 y ASP.NET Development Server. Aparece la página "404 - Archivo o directorio no encontrado".

¿Algunas ideas?

EDITAR:

Entendido ... Esta solución todavía no funciona en ASP.NET Development Server, pero tengo la razón por la que no funcionaba en IIS7 en mi caso.

La razón es que IIS7 tiene un escaneo de solicitudes incorporado que impone un límite de archivo de carga que tiene un valor predeterminado de 30000000 bytes (que es un poco menos de 30 MB).

Y estaba intentando cargar un archivo de tamaño 100 MB para probar la solución mencionada por Damien McGivern (con maxRequestLength = "10240", es decir, 10 MB en web.config). Ahora, si subo el archivo de tamaño> 10 MB y <30 MB, la página se redirige a la página de error especificada. Pero si el tamaño del archivo es> 30 MB, se mostrará la fea página de error incorporada que muestra "404 - Archivo o directorio no encontrado".

Entonces, para evitar esto, debes aumentar el máximo. longitud permitida del contenido de la solicitud para su sitio web en IIS7. Eso se puede hacer usando el siguiente comando,

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

He establecido el máximo. longitud del contenido a 200 MB.

Después de realizar esta configuración, la página se redirige con éxito a mi página de error cuando intento cargar un archivo de 100 MB

Consulte http://weblogs.asp.net/jgalloway/archive/2008/01/08/large-file-uploads-in-asp-net.aspx para obtener más detalles.

Vinod T. Patil
fuente
¡Lo siento! He agregado mi consulta como respuesta, no sé cómo agregar comentarios a publicaciones existentes.
Vinod T. Patil
1
Solo necesitas más repeticiones. para comentar publicaciones. Consulte las preguntas frecuentes para obtener más detalles sobre lo que puede o no puede hacer con su representante actual.
slaphappy
7

Aquí hay una forma alternativa, que no implica ningún "truco", pero requiere ASP.NET 4.0 o posterior:

//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
fuente
4

Una forma de hacerlo es establecer el tamaño máximo en web.config como ya se ha indicado anteriormente, por ejemplo

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

luego, cuando maneje el evento de carga, verifique el tamaño y si supera una cantidad específica, puede atraparlo, por ejemplo

protected void btnUploadImage_OnClick(object sender, EventArgs e)
{
    if (fil.FileBytes.Length > 51200)
    {
         TextBoxMsg.Text = "file size must be less than 50KB";
    }
}
BaggieBoy
fuente
1
Esto no funciona. El evento de clic nunca se activa. La excepción ocurre antes.
Lombas
3

En IIS 7 y posteriores:

archivo web.config:

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

Luego puede verificar el código subyacente, así:

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

Solo asegúrese de que el [Tamaño en bytes] en web.config sea mayor que el tamaño del archivo que desea cargar, entonces no obtendrá el error 404. Luego puede verificar el tamaño del archivo en el código detrás usando ContentLength, que sería mucho mejor

Walery Strauch
fuente
2

Como probablemente sepa, la longitud máxima de la solicitud se configura en DOS lugares.

  1. maxRequestLength - controlado a nivel de la aplicación ASP.NET
  2. maxAllowedContentLength- bajo <system.webServer>, controlado a nivel IIS

El primer caso está cubierto por otras respuestas a esta pregunta.

Para atrapar EL SEGUNDO , debe hacer esto en 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
fuente
1

Después de la etiqueta

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

agregue la siguiente etiqueta

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

puede agregar la URL a la página de error ...

Diego
fuente
0

Puede resolver esto aumentando la duración máxima de la solicitud y el tiempo de espera de ejecución en su web.config:

-Por favor, aclare el tiempo máximo de ejecución más grande que 1200

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

¿Qué tal atraparlo en el evento 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
fuente
0

Se puede comprobar a través de:

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

                return;

            }
        }
Raghav
fuente