Como posso pegar um 404?

93

Eu tenho o seguinte código:

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

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

Como posso detectar um erro 404 específico? O WebExceptionStatus.ProtocolError só pode detectar a ocorrência de um erro, mas não fornece o código exato do erro.

Por exemplo:

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

Simplesmente não é útil o suficiente ... a exceção de protocolo pode ser 401, 503, 403, qualquer coisa na verdade.

JL.
fonte
13
NNNOOOOOOOOOOOOO! Não pegue System.Exceptione não dependa do texto de exceção em seu manipulador!
Aaronaught
2
A resposta de John Saunders foi a mais completa. Acho que as pessoas apenas o rebaixaram por maldade.
Aaronaught
3
Não use throw ex, você gerará uma nova exceção com uma pilha de chamadas vazia. Basta usar throw.
krbnr
1
Sempre achei isso frustrante. Uma exceção não deve ser lançada se você obtiver uma resposta bem formada e uma mensagem de erro de protocolo for definitivamente bem formada. A classe deve permitir que o usuário interprete os resultados e aja de acordo.
Jeremy Holovacs
As exceções de @JeremyHolovacs não são mais lançadas para coisas como 404 em clientes http mais recentes. "Não use exceções para controlar o fluxo" não parecia sobreviver à equipe que construiuWebRequest
Matt Kocaj

Respostas:

113

Use o HttpStatusCode Enumeration, especificamenteHttpStatusCode.NotFound

Algo como:

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

Onde
weestá um WebException.

Jay Riggs
fonte
posso obter o NUMBER de alguma forma dos objetos sem fazer minha própria lista de pesquisa? Eu gostaria de ter algo como: inthttpresponsecode = HttpStatusCode.ToInt () ou semelhante, então recebo 404
BerggreenDK
2
@BerggreenDK você deve ser capaz de apenas fazer int httpresonsecode = (int) HttpStatusCode.NotFound
Trev
7
-1 Explicação parcial do meu antigo downvote: o código lança NullReferenceException se, por algum motivo, we.Responsenão for HttpWebResponse. Se o código deseja assumir que terá sempre desse tipo, então ele deve simplesmente lançar: HttpWebResponse errorResponse = (HttpWebResponse)we.Response;. Isso vai lançar um explícito InvalidCastExceptionse o impossível acontecer, em vez de um misterioso NullReferenceException.
John Saunders,
Eu começo a 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
fonte
10
lol @ sendo o policial identificável e dando a todos um -1 por não envolver a resposta em um bloco de uso.
Rico
2
É um trabalho difícil, mas alguém tem que fazer. OTOH, eu quase não acrescentei essa resposta, já que pode parecer que eu estava enganando todo mundo para fazer da minha a resposta com melhor classificação.
John Saunders de
3
Na verdade, votei a favor, mas só percebi uma coisa: provavelmente deve haver um throw(relançar) no final do seu catch, caso contrário, ele irá comer qualquer outro tipo de WebException.
Aaronaught
@John Saunders: Por que você também não usa o seu pedido?
Joel Etherton
1
@Joel: WebRequestnão implementa IDisposable.
John Saunders
23

Em C # 6, você pode usar filtros de exceção .

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
}
jogos artesanais
fonte
Um recurso muito legal que eu estava esquecendo! Continuei buscando métodos para capturar apenas 401 enquanto deixava outros passarem para o manipulador de exceção geral. Este é o caminho a seguir!
Lionet Chen
12

Eu não testei isso, mas deve 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
fonte
@John Saunders - Eu estava adaptando o código do OP, não otimizando.
MiffTheFox
@John - E talvez eu estivesse apenas esperando que eles copiassem / colassem o catchbloco, visto que eu tinha exatamente o mesmo código na tentativa que o OP. Você realmente deveria estar minimizando esta questão por causa do código do OP então.
MiffTheFox
1
@John esquecemos que aqui está o código de amostra. Nesse caso, é outra maneira de fazer o 404, não como usar a GetResponse. -1 parece um pouco duro. +1 para Miff por responder à pergunta.
David Basarab
@John Acho bom você apontar isso em um comentário. A maneira como vejo a votação negativa é se o código fornecido não resolve o problema. Obrigado por remover o voto negativo.
David Basarab
@John - Tudo bem, eu me livrei de tudo menos do problema, feliz agora?
MiffTheFox
4

Acho que se você pegar uma WebException, há algumas informações lá que você pode usar para determinar se foi um 404. Essa é a única maneira que conheço no momento ... Eu estaria interessado em conhecer outras ...

catch(WebException e) {
    if(e.Status == WebExceptionStatus.ProtocolError) {
        var statusCode = (HttpWebResponse)e.Response).StatusCode);
        var description = (HttpWebResponse)e.Response).StatusDescription);
    }
}
mezóide
fonte
2

Confira este snipit. O GetResponse lançará uma WebRequestException. Pegue isso e você pode obter o código de status da resposta.

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

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

Jonathan S.
fonte
2

Capture o tipo de exceção adequado 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
fonte
@John Saunders Não discuto com você aí, mas essa não era a questão, ele perguntou a melhor maneira de capturar um 404. Minhas alterações em seu código se limitaram a responder a pergunta, para tornar a mudança tão simples e óbvia quanto possível.
Nick Craver
@John Saunders: Corrigido, suponho que "se este for o mais eficiente" faz com que se aplique à pergunta.
Nick Craver
Só tive que lançar o e.Responseas HttpWebResponseantes de obter acesso ao StatusCode.
Lankymart
2

Veja em MSDN sobre o status da resposta:

...
catch(WebException e) {
  Console.WriteLine("The following error occured : {0}",e.Status);  
}
...
Dror
fonte
2
@John Saunders - Ficarei mais do que feliz em repassá-lo ao MSDN (de onde copiei a amostra de ...). O objetivo deste código é mostrar o uso de StatusCode, não ser o mais eficiente possível.
Dror
2
@John Saunders - Deixei apenas a parte que queria mostrar, Só para você :-)
Dror
2

Para o pessoal do VB.NET que está navegando aqui, acredito que podemos detectar a exceção apenas se for realmente um 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
desconhecido
fonte
1

quando POST ou GET dados para o servidor usando a classe WebRequest, o tipo de exceção seria WebException. Abaixo está o código para exceção de arquivo não 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
fonte