API REST 404: URI incorreto ou recurso ausente?

219

Estou criando uma API REST, mas encontrei um problema.

Parece que a prática aceita no design de uma API REST é que, se o recurso solicitado não existir, um 404 será retornado.

No entanto, para mim, isso adiciona ambiguidade desnecessária. O HTTP 404 é mais tradicionalmente associado a um URI incorreto. Então, com efeito, estamos dizendo: "Você chegou ao lugar certo, mas esse registro específico não existe, ou não existe esse local nos Internets! Não tenho certeza de qual deles ..."

Considere o seguinte URI:

http://mywebsite/api/user/13

Se eu receber um 404 de volta, é porque o Usuário 13 não existe? Ou é porque meu URL deveria ter sido:

http://mywebsite/restapi/user/13

No passado, acabei de retornar um resultado NULL com um HTTP 200 OKcódigo de resposta, se o registro não existir. É simples e, na minha opinião, muito limpo, mesmo que não seja necessariamente uma prática aceita. Mas existe uma maneira melhor de fazer isso?

Brian Lacy
fonte
Provavelmente uma duplicata. stackoverflow.com/questions/3821663/…
Spencer Kormos
7
A outra pergunta parece estar relacionada aos formatos de cadeia de consulta URI. A discussão sobre 404 não é suficiente para responder à minha pergunta, que é se existe uma maneira mais apropriada ou útil de determinar o que um 404 realmente significa. Eu revi esse antes de postar.
Brian Lacy
É normal quando o navegador concole consiste nos erros 404? Quando faço operações regulares com a uri correta, mas o recurso não foi encontrado.
Vasiliy Mazhekin
3
404 não indica um URI incorreto, indica um recurso não encontrado. Isso pode ser porque não há usuário '13' ou porque não há resource / mywebsite / api.
ChrisV

Respostas:

101

404 é apenas o código de resposta HTTP. Além disso, você pode fornecer a um corpo de resposta e / ou outros cabeçalhos uma mensagem de erro mais significativa que os desenvolvedores verão.

Robert Levy
fonte
7
Isso faz sentido. Eu tenho que pensar, no entanto, se algum benefício é realmente ganho ao retornar o 404 em primeiro lugar, em vez de retornar um 200 com uma resposta nula?
Brian Lacy
15
Se você retornar um 200 com um valor nulo, estará indo contra a especificação HTTP. Você pode fazer isso, mas sua API não aderirá à restrição de "Interface Uniformada" do REST.
processando
5
... e seu corpo de resposta deve incluir hiperlinks para URIs válidos. Salvo o URI raiz, e talvez um marcador ou dois, seus clientes devem sempre seguir os links fornecidos pelo servidor. Então não há necessidade de inventar semânticas detalhadas sobre exatamente como eles decidiram trabalhar fora do sistema.
Fumanchu 30/03/12
@DarrylHebbes, como assim, posso fazer uma solicitação e ver o conteúdo completo da resposta na guia Rede do console do desenvolvedor do Chrome.
jaapz
É incrivelmente ruim retornar um erro (404 ou o que for) para qualquer consulta que retorne resultados vazios. Nenhum banco de dados fará isso e, geralmente, você não deseja relatar um resultado vazio como uma condição inesperada para o usuário. Exemplo: você consulta sua API para verificar se há um compromisso agendado para os próximos 30 minutos. Se não houver, você NÃO deve retornar um 404. Retorne um 200 OK e um array vazio, por favor.
esmiralha
59

Use 404se o recurso não existir. Não volte 200com o corpo vazio.

Isso é semelhante a undefinedvs string vazia (por exemplo "") na programação. Embora muito parecido, há definitivamente uma diferença.

404significa que nada existe nesse URI (como uma variável indefinida na programação). Retornar 200com um corpo vazio significa que algo existe lá e que algo está vazio agora (como uma string vazia na programação).

404não significa que foi um "URI ruim". Existem códigos HTTP especiais destinados a erros de URI (por exemplo 414 Request-URI Too Long).

nategood
fonte
1
Ei, acho que você pode gostar de algo. Não faria mais sentido retornar um dos "41"? erros quando há um problema com o recurso solicitado? Por exemplo, 410 Gone significa "O recurso solicitado não está mais disponível no servidor e nenhum endereço de encaminhamento é conhecido". - (Consulte w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.11 )
Brian Lacy
Na verdade, o recurso que mencionei acima também diz: "Se o servidor não souber ou não tiver a capacidade de determinar se a condição é permanente ou não, o código de status 404 (Não encontrado) DEVE ser usado." Talvez isso não é necessariamente a melhor opção quer ..
Brian Lacy
2
Não acho que nenhum dos erros 41x seja apropriado para o seu caso de uso. Gosto da comparação entre a sequência indefinida e a vazia, que é uma versão mais concisa do meu argumento na minha resposta. Há uma diferença, mas isso não implica que uma string vazia seja um erro. Para esticar a analogia: Você poderia ter um método String getName()que tem uma implementação como esta: return this.name == null ? "" : this.name. Isso não é incorreta a menos que você deseja que o cliente sabe que this.nameé null.
jhericks
1
404 ainda é a opção mais apropriada. Você pode (e é incentivado a) usar o corpo dessa resposta 404 para informar o usuário / cliente de detalhes específicos para receber o erro. Consulte: stackoverflow.com/a/9999335/105484 . No seu caso, você pode querer usar o corpo para sugerir ao usuário que reformatar seu URI para olhar como / restapi / user / USER_ID
nategood
Ok, eu posso ver alguns bons pontos aqui, mas 4XX são códigos de erro, como isso é uma situação de erro? Como o cliente pode impedir que 404 aconteça?
Krzysztof Kubicki 03/04
20

Como na maioria das coisas, "depende". Mas para mim, sua prática não é ruim e não é contrária às especificações HTTP em si . No entanto, vamos esclarecer algumas coisas.

Primeiro, os URIs devem ser opacos. Mesmo que não sejam opacos para as pessoas, são opacos para as máquinas. Em outras palavras, a diferença entre http://mywebsite/api/user/13, http://mywebsite/restapi/user/13é a mesma que a diferença entre http://mywebsite/api/user/13e, http://mywebsite/api/user/14portanto, não é a mesma, não é o mesmo período. Portanto, um 404 seria completamente apropriado parahttp://mywebsite/api/user/14 (se não houver esse usuário), mas não necessariamente a única resposta apropriada.

Você também pode retornar uma resposta 200 vazia ou, mais explicitamente, uma resposta 204 (sem conteúdo). Isso transmitiria algo mais ao cliente. Isso implica que o recurso identificado por http://mywebsite/api/user/14não possui conteúdo ou é essencialmente nada. Isso significa que não é um tal recurso. No entanto, isso não significa necessariamente que você está reivindicando a existência de algum usuário em um repositório de dados com o ID 14. Essa é sua preocupação particular, não a preocupação do cliente que faz a solicitação. Portanto, se faz sentido modelar seus recursos dessa maneira, vá em frente.

Existem algumas implicações de segurança em fornecer aos seus clientes informações que facilitariam a adivinhação de URIs legítimos. O retorno de 200 em caso de falha em vez de 404 pode dar ao cliente uma pista de que pelo menos a http://mywebsite/api/userpeça está correta. Um cliente mal-intencionado pode continuar tentando números inteiros diferentes. Mas para mim, um cliente mal-intencionado seria capaz de adivinhar a http://mywebsite/api/userparte de qualquer maneira. Um remédio melhor seria usar UUID's. ou seja, http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66é melhor que http://mywebsite/api/user/14. Fazendo isso, você pode usar sua técnica de retornar 200 sem revelar muito.

jhericks
fonte
1
Esta é a solução que eu escolhi e utilizei um 204 em vez de um 404. Para mim 204 significa "Encontrei seu código, mas não encontrei resultados" e 404 significa "Não consegui encontrar nenhum código para executar, é este o caminho errado? "
Matt Sanders
11

404 Not Found tecnicamente significa que uri atualmente não mapear a um recurso. No seu exemplo, interpreto uma solicitação http://mywebsite/api/user/13que retorna um 404 para indicar que esse URL nunca foi mapeado para um recurso. Para o cliente, esse deve ser o fim da conversa.

Para lidar com preocupações com ambiguidade, você pode aprimorar sua API fornecendo outros códigos de resposta. Por exemplo, suponha que você deseja permitir que os clientes emitam solicitações GET do URL http://mywebsite/api/user/13, você deseja comunicar que os clientes devem usar o URL canônico http://mywebsite/restapi/user/13. Nesse caso, convém emitir um redirecionamento permanente retornando um 301 Movido permanentemente e fornecer o URL canônico no cabeçalho Localização da resposta. Isso informa ao cliente que, para solicitações futuras, ele deve usar o URL canônico.

Ralph Allan Rice
fonte
11

Portanto, em essência, parece que a resposta pode depender de como a solicitação é formada.

Se o recurso solicitado fizer parte do URI de acordo com uma solicitação http://mywebsite/restapi/user/13e o usuário 13 não existir, provavelmente um 404 é apropriado e intuitivo porque o URI é representativo de um usuário / entidade / documento / etc. O mesmo http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66vale para a técnica mais segura usando um GUID e o argumento api / restapi acima.

No entanto, se o ID do recurso solicitado foi incluído no cabeçalho da solicitação [inclua seu próprio exemplo] ou, de fato, no URI como parâmetro, por exemplo http://mywebsite/restapi/user/?UID=13, o URI ainda estaria correto (porque o conceito de USER sai em http://mywebsite/restapi/user/); portanto, é razoável esperar que a resposta seja 200 (com uma mensagem adequadamente detalhada) porque o usuário específico conhecido como 13 não existe, mas o URI existe. Dessa forma, estamos dizendo que o URI é bom, mas a solicitação de dados não tem conteúdo.

Pessoalmente, 200 ainda não parece certo (embora eu já tenha argumentado anteriormente). Um código de resposta 200 (sem uma resposta detalhada) pode fazer com que um problema não seja investigado quando um ID incorreto é enviado, por exemplo.

Uma abordagem melhor seria enviar uma 204 - No Contentresposta. Isso é compatível com a descrição do w3c. * O servidor atendeu à solicitação, mas não precisa retornar um corpo de entidade e pode retornar informações metainformadas. * 1 A confusão, na minha opinião, é causada pela entrada da Wikipedia que indica 204 No Content - O servidor processou a solicitação com êxito, mas não está retornando nenhum conteúdo. Geralmente usado como resposta a uma solicitação de exclusão bem-sucedida .A última frase é altamente discutível. Considere a situação sem essa frase e a solução é fácil - basta enviar um 204 se a entidade não existir. Existe até um argumento para retornar 204 em vez de 404, a solicitação foi processada e nenhum conteúdo foi retornado! Esteja ciente de que os 204 não permitem conteúdo no corpo da resposta

Fontes

http://en.wikipedia.org/wiki/List_of_HTTP_status_codes 1. http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

John
fonte
9

Esse é um post muito antigo, mas enfrentei um problema semelhante e gostaria de compartilhar minha experiência com vocês.

Estou construindo uma arquitetura de microsserviço com APIs restantes. Eu tenho alguns serviços GET em repouso, eles coletam dados do sistema back-end com base nos parâmetros de solicitação.

Segui os demais documentos de design da API e enviei HTTP 404 com uma mensagem de erro JSON perfeita para o cliente quando não havia dados alinhados às condições da consulta (por exemplo, zero registro foi selecionado).

Quando não havia dados a serem devolvidos ao cliente, preparei uma mensagem JSON perfeita com código de erro interno etc. para informar o cliente sobre o motivo do "Não encontrado" e ele foi enviado de volta ao cliente com HTTP 404. Isso funciona bem.

Posteriormente, criei uma classe de cliente da API Rest, que é um auxiliar fácil para ocultar o código relacionado à comunicação HTTP e usei esse auxiliar o tempo todo quando chamei minhas APIs de descanso do meu código.

MAS eu precisava escrever um código extra confuso apenas porque o HTTP 404 tinha duas funções diferentes:

  • o HTTP 404 real quando a API restante não está disponível no URL fornecido, é lançada pelo servidor de aplicativos ou servidor da web em que o aplicativo API restante é executado
  • o cliente recebe HTTP 404 também quando não há dados no banco de dados com base na condição where da consulta.

Importante: Meu manipulador de erros da API de descanso captura todas as exceções que aparecem no serviço de back-end, o que significa que, em caso de erro, minha API de descanso sempre retorna com uma mensagem JSON perfeita com os detalhes da mensagem.

Esta é a primeira versão do meu método auxiliar de cliente que lida com as duas respostas HTTP 404 diferentes:

public static String getSomething(final String uuid) {
    String serviceUrl = getServiceUrl();
    String path = "user/" + , uuid);
    String requestUrl = serviceUrl + path;
    String httpMethod = "GET";

    Response response = client
            .target(serviceUrl)
            .path(path)
            .request(ExtendedMediaType.APPLICATION_UTF8)
            .get();

    if (response.getStatus() == Response.Status.OK.getStatusCode()) {
        // HTTP 200
        return response.readEntity(String.class);
    } else {
        // confusing code comes here just because
        // I need to decide the type of HTTP 404...

        // trying to parse response body
        try {
            String responseBody = response.readEntity(String.class);
            ObjectMapper mapper = new ObjectMapper();
            ErrorInfo errorInfo = mapper.readValue(responseBody, ErrorInfo.class);

            // re-throw the original exception
            throw new MyException(errorInfo);

        } catch (IOException e) {
            // this is a real HTTP 404
            throw new ServiceUnavailableError(response, requestUrl, httpMethod);
        }

    // this exception will never be thrown
    throw new Exception("UNEXPECTED ERRORS, BETTER IF YOU DO NOT SEE IT IN THE LOG");
}

MAS , porque meu cliente Java ou JavaScript pode receber dois tipos de HTTP 404 de alguma forma, preciso verificar o corpo da resposta no caso do HTTP 404. Se eu puder analisar o corpo da resposta, tenho certeza de que recebi de volta uma resposta onde havia sem dados para enviar de volta ao cliente.

Se não conseguir analisar a resposta, significa que recebi um HTTP 404 real do servidor da Web (não do aplicativo API restante).

É tão confuso e o aplicativo cliente sempre precisa fazer uma análise extra para verificar o real motivo do HTTP 404.

Honestamente, eu não gosto desta solução. É confuso, precisa adicionar código extra de besteira aos clientes o tempo todo.

Portanto, em vez de usar o HTTP 404 nesses dois cenários diferentes, decidi que faria o seguinte:

  • Não estou mais usando HTTP 404 como código HTTP de resposta no meu aplicativo de descanso.
  • Vou usar o HTTP 204 (sem conteúdo) em vez do HTTP 404.

Nesse caso, o código do cliente pode ser mais elegante:

public static String getString(final String processId, final String key) {
    String serviceUrl = getServiceUrl();
    String path = String.format("key/%s", key);
    String requestUrl = serviceUrl + path;
    String httpMethod = "GET";

    log(requestUrl);

    Response response = client
            .target(serviceUrl)
            .path(path)
            .request(ExtendedMediaType.APPLICATION_JSON_UTF8)
            .header(CustomHttpHeader.PROCESS_ID, processId)
            .get();

    if (response.getStatus() == Response.Status.OK.getStatusCode()) {
        return response.readEntity(String.class);
    } else {
        String body = response.readEntity(String.class);
        ObjectMapper mapper = new ObjectMapper();
        ErrorInfo errorInfo = mapper.readValue(body, ErrorInfo.class);
        throw new MyException(errorInfo);
    }

    throw new AnyServerError(response, requestUrl, httpMethod);
}

Eu acho que isso lida melhor com esse problema.

Se você tiver alguma solução melhor, compartilhe-a conosco.

zappee
fonte
Os métodos Apache HttpClient não lançam exceção em uma resposta 204. Se o seu cliente responder apenas aos objetos de negócios (modelos), isso retornará nulo. Funciona, mas é difícil para os usuários finais detectar a situação 204.
Chrisinmtown
Está tudo bem, mas uma pergunta é: com que frequência você escreve declarações para o 404? E o que seria? Por outro lado, se você fizer uma chamada para a API, que define possível 404 com o erro json dentro, poderá lidar com isso apenas nesse caso.
Max
zappee Eu concordo com você. Eu tenho um serviço que retorna dados. Mas há situações em que os dados são quebrados. O URL da solicitação está correto (portanto, não um 404), mas não é realmente um erro lógico (500), portanto 204 parece ser o mais adequado. É realmente irritante a falta de uma mensagem no corpo da mensagem. Embora, sem dúvida, eu pudesse inserir um motivo em um cabeçalho.
cs94njw
6

Este artigo antigo, mas excelente ... http://www.infoq.com/articles/webber-rest-workflow diz isso sobre isso ...

404 Não encontrado - O serviço é muito preguiçoso (ou seguro) para nos dar uma verdadeira razão pela qual nossa solicitação falhou, mas seja qual for a razão, precisamos lidar com isso.

Michael Dausmann
fonte
1

O identificador uniforme de recursos é um ponteiro exclusivo para o recurso. Um URI de formato incorreto não aponta para o recurso e, portanto, executar um GET nele não retornará um recurso. 404 significa que o servidor não encontrou nada correspondente ao URI da solicitação. Se você inserir o URI errado ou o URI incorreto, esse é o seu problema e o motivo pelo qual você não acessou um recurso, seja uma página HTML ou IMG.

processando
fonte
0

Nesse cenário, o HTTP 404 é um código de resposta para a resposta da API REST, como entidade não processável 400, 401, 404, 422

use o tratamento de exceções para verificar a mensagem de exceção completa.

try{
  // call the rest api
} catch(RestClientException e) {
     //process exception
     if(e instanceof HttpStatusCodeException){
        String responseText=((HttpStatusCodeException)e).getResponseBodyAsString();
         //now you have the response, construct json from it, and extract the errors
         System.out.println("Exception :" +responseText);
     }

}

Esse bloco de exceção fornece a mensagem correta lançada pela API REST

Venkata Naresh Babu
fonte