REST API 404: ¿URI incorrecto o recurso faltante?

Estoy construyendo una API REST, pero he encontrado un problema.

Parece que la práctica aceptada al diseñar una API REST es que si el recurso solicitado no existe, se devuelve 404.

Sin embargo, para mí, esto agrega una ambigüedad innecesaria. HTTP 404 está asociado más tradicionalmente con un URI incorrecto. Así que, en realidad, estamos diciendo “O llegaste al lugar correcto, pero ese registro específico no existe, o no hay tal ubicación en Internets. Realmente no estoy seguro de cuál …”

Considere el siguiente URI:

http://mywebsite/api/user/13 

Si recupero un 404, ¿es porque el Usuario 13 no existe? O es porque mi URL debería haber sido:

 http://mywebsite/restapi/user/13 

En el pasado, acabo de devolver un resultado NULO con un código de respuesta HTTP 200 OK si el registro no existe. Es simple, y en mi opinión muy limpio, incluso si no es necesariamente una práctica aceptada. ¿Pero hay una mejor manera de hacer esto?

404 es solo el código de respuesta HTTP. Además de eso, puede proporcionar un cuerpo de respuesta y / u otros encabezados con un mensaje de error más significativo que los desarrolladores verán.

Use 404 si el recurso no existe. No devuelva 200 con un cuerpo vacío.

Esto es similar a cadena undefined vs cadena vacía (por ejemplo, "" ) en la progtwigción. Si bien es muy similar, definitivamente hay una diferencia.

404 significa que no existe nada en ese URI (como una variable indefinida en la progtwigción). Devolver 200 con un cuerpo vacío significa que algo existe allí y que algo está simplemente vacío en este momento (como una cadena vacía en la progtwigción).

404 no significa que fue un “URI malo”. Hay códigos HTTP especiales destinados a errores de URI (por ejemplo, 414 Request-URI Too Long ).

Como con la mayoría de las cosas, “depende”. Pero para mí, su práctica no es mala y no va en contra de las especificaciones HTTP per se . Sin embargo, aclaremos algunas cosas.

Primero, los URI deben ser opacos. Incluso si no son opacos para las personas, son opacos para las máquinas. En otras palabras, la diferencia entre http://mywebsite/api/user/13 , http://mywebsite/restapi/user/13 es la misma que la diferencia entre http://mywebsite/api/user/13 y http://mywebsite/api/user/14 es decir, no es lo mismo no es el mismo período. Entonces, un 404 sería completamente apropiado para http://mywebsite/api/user/14 (si no hay tal usuario) pero no necesariamente la única respuesta apropiada.

También puede devolver una respuesta vacía 200 o más explícitamente una respuesta 204 (Sin contenido). Esto le transmitiría algo más al cliente. Implicaría que el recurso identificado por http://mywebsite/api/user/14 no tiene contenido o es esencialmente nada. Significa que hay tal recurso. Sin embargo, esto no significa necesariamente que esté reclamando que persiste algún usuario en un almacén de datos con ID 14. Esa es su preocupación privada, no la preocupación del cliente que realiza la solicitud. Entonces, si tiene sentido modelar sus recursos de esa manera, adelante.

Existen algunas implicaciones de seguridad para dar a los clientes información que les facilitaría adivinar los URI legítimos. Si devuelve un 200 en errores en lugar de un 404 puede dar al cliente una pista de que al menos la parte http://mywebsite/api/user es correcta. Un cliente malicioso podría seguir probando diferentes enteros. Pero para mí, un cliente malicioso podría adivinar la parte http://mywebsite/api/user todos modos. Un mejor remedio sería usar UUID. es decir, http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66 es mejor que http://mywebsite/api/user/14 . Al hacer eso, podría usar su técnica de devolver los 200 sin dar mucha distancia.

404 Not Found técnicamente significa que uri no se asigna actualmente a un recurso. En su ejemplo, interpreto una solicitud a http://mywebsite/api/user/13 que devuelve un 404 para indicar que esta url nunca se asignó a un recurso. Para el cliente, ese debería ser el final de la conversación.

Para abordar las preocupaciones con ambigüedad, puede mejorar su API proporcionando otros códigos de respuesta. Por ejemplo, supongamos que desea permitir que los clientes emitan solicitudes GET en la URL http://mywebsite/api/user/13 , desea comunicar que los clientes deben usar la URL canónica http://mywebsite/restapi/user/13 . En ese caso, es posible que desee considerar la posibilidad de emitir un redireccionamiento permanente devolviendo un 301 Movido permanentemente y proporcionar la url canónica en el encabezado de Ubicación de la respuesta. Esto le dice al cliente que para futuras solicitudes deben usar la url canónica.

Este artículo antiguo pero excelente … http://www.infoq.com/articles/webber-rest-workflow dice esto al respecto …

404 no encontrado: el servicio es demasiado vago (o seguro) para darnos una razón real de por qué nuestra solicitud falló, pero sea cual sea el motivo, debemos tratar con eso.

Entonces, en esencia, parece que la respuesta podría depender de cómo se formó la solicitud.

Si el recurso solicitado forma parte del URI según una solicitud a http://mywebsite/restapi/user/13 y el usuario 13 no existe, entonces un 404 es probablemente apropiado e intuitivo porque el URI es representativo de un inexistente usuario / entidad / documento / etc. Lo mismo ocurriría con la técnica más segura que utiliza un GUID http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66 y el argumento api / restapi anterior.

Sin embargo, si el ID del recurso solicitado se incluyó en el encabezado de solicitud [incluya su propio ejemplo], o bien, en el URI como parámetro, por ejemplo, http://mywebsite/restapi/user/?UID=13 entonces el URI aún sea ​​correcto (porque el concepto de USUARIO sí sale en http://mywebsite/restapi/user/ ); y, por lo tanto, se podría esperar razonablemente que la respuesta sea 200 (con un mensaje apropiadamente detallado) porque el usuario específico conocido como 13 no existe, pero sí el URI. De esta manera, estamos diciendo que el URI es bueno, pero la solicitud de datos no tiene contenido.

Personalmente, un 200 todavía no se siente bien (aunque previamente he argumentado que sí). Un código de respuesta 200 (sin una respuesta detallada) podría causar que un problema no se investigue cuando se envía una ID incorrecta, por ejemplo.

Un mejor enfoque sería enviar un 204 - No Content respuesta de 204 - No Content . Esto es compatible con la descripción de w3c * El servidor ha cumplido la solicitud pero no necesita devolver un cuerpo de entidad, y podría querer devolver metainformación actualizada. * 1 La confusión, en mi opinión, es causada por la entrada de Wikipedia que indica 204 Sin contenido – El servidor procesó correctamente la solicitud, pero no devuelve ningún contenido. Usualmente se usa como respuesta a una solicitud de eliminación exitosa . La última oración es altamente discutible. Considere la situación sin esa oración y la solución es fácil: solo envíe un 204 si la entidad no existe. Incluso hay un argumento para devolver un 204 en lugar de un 404, la solicitud se ha procesado y no se ha devuelto ningún contenido. Sin embargo, tenga en cuenta que los 204 no permiten contenido en el cuerpo de respuesta

Fuentes

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

Esa es una publicación muy antigua, pero me enfrenté a un problema similar y me gustaría compartir mi experiencia con ustedes.

Estoy construyendo architecture de microservicio con API de descanso. Tengo algunos servicios GET de descanso, ellos recostackn datos del sistema de back-end basados ​​en los parámetros de solicitud.

Seguí el rest de los documentos de diseño de API y envié HTTP 404 con un mensaje de error JSON perfecto al cliente cuando no había datos que se alinearan con las condiciones de consulta (por ejemplo, se seleccionó cero registro).

Cuando no había datos para enviar al cliente, preparé un mensaje JSON perfecto con código de error interno, etc. para informar al cliente sobre el motivo del “No encontrado” y se lo devolvió al cliente con HTTP 404. Eso funciona bien.

Más tarde, he creado una clase de cliente de API de reposo que es una ayuda fácil para ocultar el código relacionado con la comunicación HTTP y usé esta ayuda todo el tiempo cuando llamé a mis API de reposo desde mi código.

PERO necesitaba escribir código extra confuso solo porque HTTP 404 tenía dos funciones diferentes:

  • el HTTP 404 real cuando la API restante no está disponible en la URL determinada, es lanzada por el servidor de aplicaciones o el servidor web donde se ejecuta la aplicación de API restante
  • el cliente también recupera HTTP 404 cuando no hay datos en la base de datos en función de la condición where de la consulta.

Importante: Mi controlador de error de la API de reposo detecta todas las excepciones que aparecen en el servicio de fondo, lo que significa que, en caso de error, mi API de descanso siempre regresa con un mensaje JSON perfecto con los detalles del mensaje.

Esta es la primera versión de mi método de ayuda al cliente que maneja las dos respuestas 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"); } 

PERO , dado que mi cliente Java o JavaScript puede recibir dos tipos de HTTP 404, de alguna manera tengo que verificar el cuerpo de la respuesta en el caso de HTTP 404. Si puedo analizar el cuerpo de la respuesta, estoy seguro de que recibí una respuesta donde había no hay datos para enviar de vuelta al cliente.

Si no puedo analizar la respuesta, significa que obtuve un HTTP 404 real del servidor web (no del rest de la aplicación API).

Es muy confuso y la aplicación del cliente siempre necesita realizar un análisis adicional para verificar la verdadera razón del HTTP 404.

Honestamente, no me gusta esta solución. Es confuso, necesita agregar código extra de mierda a los clientes todo el tiempo.

Entonces, en lugar de usar HTTP 404 en estos dos escenarios diferentes, decidí que haré lo siguiente:

  • Ya no estoy utilizando HTTP 404 como código de respuesta HTTP en mi aplicación de descanso.
  • Voy a usar HTTP 204 (Sin contenido) en lugar de HTTP 404.

En ese caso, el código del cliente puede ser más 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); } 

Creo que esto maneja mejor ese problema.

Si tiene una mejor solución, por favor, compártalo con nosotros.

El Identificador Uniforme de Recursos es un puntero único al recurso. Un URI de forma deficiente no apunta al recurso y, por lo tanto, realizar un GET no devolverá un recurso. 404 significa que el servidor no ha encontrado nada que coincida con el URI de solicitud. Si coloca el URI incorrecto o el URI incorrecto, ese es su problema y la razón por la que no llegó a un recurso, ya sea una página HTML o IMG.