¿Cómo puedo atrapar un 404?

93

Tengo el siguiente código:

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "HEAD";
request.Credentials = MyCredentialCache;

try
{
    request.GetResponse();
}
catch
{
}

¿Cómo puedo detectar un error 404 específico? WebExceptionStatus.ProtocolError solo puede detectar que ocurrió un error, pero no proporcionar el código exacto del error.

Por ejemplo:

catch (WebException ex)
{
    if (ex.Status != WebExceptionStatus.ProtocolError)
    {
        throw ex;
    }
}

No es lo suficientemente útil ... la excepción del protocolo podría ser 401, 503, 403, cualquier cosa en realidad.

JL.
fuente
13
¡NNNOOOOOOOOOOOOO! ¡No lo entiendas System.Exceptiony no dependas del texto de excepción en tu controlador!
Aaronaught
2
La respuesta de John Saunders fue la más completa. Creo que la gente lo rechazó por despecho.
Aaronaught
3
No lo use throw ex, generará una nueva excepción con una pila de llamadas vacía. Solo usa throw.
krbnr
1
Yo siempre he encontrado esto frustrante. No se debe lanzar una excepción si obtiene una respuesta bien formada, y un mensaje de error de protocolo definitivamente está bien formado. La clase debe permitir al usuario interpretar los resultados y actuar en consecuencia.
Jeremy Holovacs
Las excepciones de @JeremyHolovacs ya no se lanzan para cosas como 404 en clientes http más nuevos. "No use excepciones para el flujo de control" no pareció sobrevivir al equipo que construyóWebRequest
Matt Kocaj

Respuestas:

114

Utilice el HttpStatusCode Enumeration, específicamenteHttpStatusCode.NotFound

Algo como:

HttpWebResponse errorResponse = we.Response as HttpWebResponse;
if (errorResponse.StatusCode == HttpStatusCode.NotFound) {
  //
}

¿Dónde
weestá un WebException.

Jay Riggs
fuente
¿Puedo obtener el NÚMERO de alguna manera de los objetos sin hacer mi propia lista de búsqueda? Me gustaría tener algo como: int httpresponsecode = HttpStatusCode.ToInt () o similar para obtener 404
BerggreenDK
2
@BerggreenDK debería poder simplemente hacer int httpresonsecode = (int) HttpStatusCode.NotFound
Trev
8
-1 Explicación parcial de mi antiguo voto negativo: el código arroja NullReferenceException si, por alguna razón, we.Responseno lo es HttpWebResponse. Si el código desea asumir que siempre va a tener ese tipo, entonces simplemente echa: HttpWebResponse errorResponse = (HttpWebResponse)we.Response;. Esto arrojará un explícito InvalidCastExceptionsi sucede lo imposible, en lugar de un misterioso NullReferenceException.
John Saunders
Consigo An object reference is required for the non-static field, method, or property 'WebException.Response'usar este código.
Jamie
122
try
{
    var request = WebRequest.Create(uri);
    using (var response = request.GetResponse())
    {
        using (var responseStream = response.GetResponseStream())
        {
            // Process the stream
        }
    }
}
catch (WebException ex)
{
    if (ex.Status == WebExceptionStatus.ProtocolError &&
        ex.Response != null)
    {
        var resp = (HttpWebResponse) ex.Response;
        if (resp.StatusCode == HttpStatusCode.NotFound)
        {
            // Do something
        }
        else
        {
            // Do something else
        }
    }
    else
    {
        // Do something else
    }
}
John Saunders
fuente
10
lol @ siendo la policía de IDisposable y dando a todos un -1 por no envolver la respuesta en un bloque de uso.
Rich
2
Es un trabajo duro, pero alguien tiene que hacerlo. OTOH, casi no agregué esta respuesta, ya que podría parecer que estaba molestando a todos los demás para hacer de la mía la respuesta mejor calificada.
John Saunders
3
De hecho, voté a favor, pero solo noté una cosa: probablemente debería haber un throw(relanzamiento) al final de tu catch, de lo contrario, esto simplemente comerá silenciosamente cualquier otro tipo de WebException.
Aaronaught
@John Saunders: ¿Por qué no utilizas también tu solicitud?
Joel Etherton
1
@Joel: WebRequestno implementa IDisposable.
John Saunders
23

En C # 6 puede usar filtros de excepción .

try
{
    var request = WebRequest.Create(uri);
    using (var response = request.GetResponse())
    using (var responseStream = response.GetResponseStream())
    {
        // Process the stream
    }
}
catch(WebException ex) when ((ex.Response as HttpWebResponse)?.StatusCode == HttpStatusCode.NotFound)
{
    // handle 404 exceptions
}
catch (WebException ex)
{
    // handle other web exceptions
}
juegos de manualidades
fuente
¡Una característica muy interesante que he estado pasando por alto! Seguí buscando métodos para detectar solo 401 mientras dejaba que otros pasaran al controlador de excepciones general. ¡Este es el camino a seguir!
Lionet Chen
12

No lo he probado, pero debería funcionar

try
{
    // TODO: Make request.
}
catch (WebException ex)
{
    if (ex.Status == WebExceptionStatus.ProtocolError) {
        HttpWebResponse resp = ex.Response as HttpWebResponse;
        if (resp != null && resp.StatusCode == HttpStatusCode.NotFound)
        {
            // TODO: Handle 404 error.
        }
        else
            throw;
    }
    else
        throw;
}
MiffTheFox
fuente
@John Saunders: estaba adaptando el código del OP, no optimizándolo.
MiffTheFox
@John - Y tal vez solo esperaba que copiaran / pegaran el catchbloque, ya que tenía exactamente el mismo código en el intento que el OP. Realmente deberías estar bajando esta pregunta por completo debido al código del OP.
MiffTheFox
1
@John olvidamos que aquí hay un código de muestra. Este es el caso de que sea otra forma de 404, no cómo usar GetResponse. -1 parece un poco duro. +1 a Miff por responder la pregunta.
David Basarab
@John Creo que es bueno que lo señales en un comentario. La forma en que veo la votación negativa es si el código proporcionado no resuelve el problema. Gracias por eliminar el voto negativo.
David Basarab
@John - Bien, me deshice de todo menos de la trampa, ¿feliz ahora?
MiffTheFox
4

Creo que si detecta una WebException, hay información que puede usar para determinar si era un 404. Esa es la única forma que conozco en este momento ... Me interesaría conocer otras ...

catch(WebException e) {
    if(e.Status == WebExceptionStatus.ProtocolError) {
        var statusCode = (HttpWebResponse)e.Response).StatusCode);
        var description = (HttpWebResponse)e.Response).StatusDescription);
    }
}
mezoide
fuente
2

Mira este snipit. GetResponse lanzará una WebRequestException. Capture eso y puede obtener el código de estado de la respuesta.

try {
   // Create a web request for an invalid site. Substitute the "invalid site" strong in the Create call with a invalid name.
     HttpWebRequest myHttpWebRequest = (HttpWebRequest) WebRequest.Create("invalid site");

    // Get the associated response for the above request.
     HttpWebResponse myHttpWebResponse = (HttpWebResponse) myHttpWebRequest.GetResponse();
    myHttpWebResponse.Close();
}
catch(WebException e) {
    Console.WriteLine("This program is expected to throw WebException on successful run."+
                        "\n\nException Message :" + e.Message);
    if(e.Status == WebExceptionStatus.ProtocolError) {
        Console.WriteLine("Status Code : {0}", ((HttpWebResponse)e.Response).StatusCode);
        Console.WriteLine("Status Description : {0}", ((HttpWebResponse)e.Response).StatusDescription);
    }
}
catch(Exception e) {
    Console.WriteLine(e.Message);
}

esto vino de http://msdn.microsoft.com/en-us/library/system.net.webexception.status.aspx

Jonathan S.
fuente
2

Detecte el tipo de excepción adecuado WebException:

try
{
    var request = (HttpWebRequest) WebRequest.Create(String.Format("http://www.gravatar.com/avatar/{0}?d=404", hashe));

    using(var response = (HttpWebResponse)request.GetResponse())
        Response.Write("has avatar");
}
catch(WebException e) 
{
  if(e.Response.StatusCode == 404) 
    Response.Write("No avatar");
}
Nick Craver
fuente
@John Saunders No debatí contigo allí, pero esa no era la pregunta, me preguntó cuál era la mejor manera de capturar un 404. Mis cambios en su código se limitaron a responder la pregunta, para hacer el cambio tan simple y obvio como posible.
Nick Craver
@John Saunders: Corregido, supongo que "si esto es lo más eficiente" hace que se aplique a la pregunta.
Nick Craver
Solo tenía que lanzar el e.Responseas HttpWebResponseantes de obtener acceso al StatusCode.
Lankymart
2

Consulte en MSDN sobre el estado de la respuesta:

...
catch(WebException e) {
  Console.WriteLine("The following error occured : {0}",e.Status);  
}
...
Dror
fuente
2
@John Saunders - Estaré más que feliz de pasárselo a MSDN (de donde copié la muestra ...). El propósito de este código es mostrar el uso de StatusCode, no ser lo más eficiente posible.
Dror
2
@John Saunders - Dejé solo la parte que quería mostrar, solo para ti :-)
Dror
2

Para la gente de VB.NET que navega por esto, creo que podemos detectar la excepción solo si realmente es un 404. Algo como:

Try
    httpWebrequest.GetResponse()
Catch we As WebException When we.Response IsNot Nothing _
                              AndAlso TypeOf we.Response Is HttpWebResponse _
                              AndAlso (DirectCast(we.Response, HttpWebResponse).StatusCode = HttpStatusCode.NotFound)

    ' ...

End Try
desconocido
fuente
1

cuando POST o GET datos al servidor usando la clase WebRequest, entonces el tipo de excepción sería WebException. A continuación se muestra el código para la excepción de archivo no encontrado

        //Create a web request with the specified URL
            string path = @"http://localhost/test.xml1";
            WebRequest myWebRequest = WebRequest.Create(path);

       //Senda a web request and wait for response.
                try
                {
                    WebResponse objwebResponse = myWebRequest.GetResponse();
                    Stream stream= objwebResponse.GetResponseStream();

                }
                catch (WebException ex) {
                    if (((HttpWebResponse)(ex.Response)).StatusCode == HttpStatusCode.NotFound) {
                        throw new FileNotFoundException(ex.Message);
                    }

                }
Sheo Dayal Singh
fuente