Diseño de API RESTful. ¿Qué debo devolver si no hay filas?

50

Actualmente estoy codificando una API para una red social con Slim Framework. Mi pregunta es: ¿Cuáles son las mejores prácticas cuando no hay filas para devolver en la estructura json?

Digamos que esta llamada / v1 / get / movies devuelve 2 filas de los nombres de películas de la tabla:

[
    {"name": "Ghostbusters"},
    {"name": "Indiana Jones"}
]

Pero, luego llamo / v1 / get / books y no hay filas en esa tabla. ¿Debo devolver una estructura vacía?

[
]

... o sería mejor un mensaje y un código de error?

[
    "errors": {
        "message": "no matches found",
        "code": 134
    }
]

¿Cuál es una mejor práctica? (la API se usará en aplicaciones iOS y Android) ¡Gracias!

Andres SK
fuente
3
Para mí, esto se parece a la pregunta de si cero es realmente una cantidad.
scarfridge
16
Tu ejemplo está roto. No puede tener objetos json con claves duplicadas. Lo que está buscando es una matriz, es decir[{"name": "..."}, {"name":"..."}]
Martin Wickman
@ MartinWickman Lo siento, lo acabo de arreglar.
Andres SK
8
@andufo, en realidad, no lo hiciste ...
avakar
25
Si su aplicación debe ser RESTful, ¿por qué el verbo / método "get" forma parte de su URI de punto final?
user50849

Respuestas:

46

Por lo general, devolvería el número de registros en resultado como metadatos. No estoy seguro de si es una práctica REST normal, pero no se trata de muchos datos adicionales y es muy precisa. Por lo general, existe una paginación para muchos servicios, no es práctico devolver un gran conjunto de resultados a la vez. Personalmente, me molesta cuando hay paginación para pequeños conjuntos de resultados. Si está vacío, regrese number_of_records : 0y reserve como lista / matriz vacía books : [].

{
    meta: {
        number_of_records : 2,
        records_per_page : 10,
        page : 0
    },
    books : [
        {id:1},
        {id:27}
    ]
}

EDITAR (pocos años después): la respuesta de Martin Wickman es mucho mejor, aquí hay una "breve" explicación de por qué.

Cuando se trata de paginación, siempre tenga en cuenta la posibilidad de que el contenido o el orden cambien. Como, la primera solicitud llega, 24 resultados, usted devuelve primero 10. Después de eso, se inserta "nuevo libro" y ahora tiene 25 resultados, pero con la solicitud original vendría ordenado en el décimo lugar. Cuando el primer usuario solicita la segunda página, no obtendría un "libro nuevo". Hay maneras de manejar tales problemas, como proporcionar "id de solicitud" que debe enviarse con las siguientes llamadas a la API, luego regresar la página siguiente del conjunto de resultados "antiguo", que debe almacenarse de alguna manera y vincularse a "id de solicitud". La alternativa es agregar un campo como "la lista de resultados ha cambiado desde la primera solicitud".

En general, si puede, intente hacer un esfuerzo adicional y evitar la paginación. La paginación es un estado adicional que puede ser mutado y el seguimiento de dichos cambios es propenso a errores, aún más porque tanto el servidor como el cliente necesitan manejarlo.

Si tiene demasiados datos para procesar a la vez , considere devolver la "lista de identificación" con todos los resultados y detalles para algunos fragmentos de esa lista, y proporcione llamadas de API multi_get / get_by_id_list para recursos.

grizwako
fuente
1
Huh, me pregunto por qué este no es tan votado como el otro. Esto me gusta más, ya que proporciona una lista vacía (que se suponía que era una lista, ¿no?) Que se puede repetir ciegamente sin condiciones especiales, pero también los metadatos cuentan como una forma de decir: "No, no lo hicimos". t pasar por alto un error, en realidad hubo 0 resultados ".
Izkata
1
-1 porque el booksparámetro es un objeto pero 'libros' implica más de uno y más de uno implica una matriz. Los metadatos son geniales y todos, pero en última instancia, esperaría que una colección de libros sea una matriz de objetos de libros; si no hay libros, solo dame la matriz vacía
Charles Sprayberry
99
El problema con esto es que agregar "número_de_registros" no proporciona más información, solo agrega redundancia y aumenta la complejidad. Para señalar un error, devuelva un código http adecuado + algo en el cuerpo.
Martin Wickman,
1
@cspray books es una lista, como señaló Izkata, mi error tipográfico.
grizwako
2
@ MartinWickman No quería contaminar la respuesta original con metadatos adicionales, pero en mi experiencia, muchos servicios no devuelven todos los datos de inmediato, sino de manera "paginada".
grizwako
105

Tu ejemplo está roto. No debería tener objetos json con claves duplicadas. Lo que está buscando es una matriz con objetos de película, como esta:

 [
    {"name": "movie1"}, 
    {"name": "movie2"}
 ]

Este enfoque también responde a su pregunta. Debería devolver una matriz vacía cuando la consulta no coincida:

[]

Por otro lado, si intenta obtener un recurso de película específicoGET api/movie/34 y esa película no existe, devuelva 404 con un mensaje de error adecuado (codificado con json) en el cuerpo

Martin Wickman
fuente
1
+1 Esto es JSON válido según json_xs.
l0b0
15

Si esto es JSON, realmente debería considerar devolver una matriz de objetos. Esto tiene muchas ventajas, incluyendo que cuando no tienes registros es una matriz vacía.

Entonces, cuando tenga registros, regresaría:

    [
        {"name": "Ghostbusters"},
        {"name": "Indiana Jones"}
    ]

Y cuando no tienes registros, regresarías:

    [

    ]
Devdatta Tengshe
fuente
14

Si ejecuta la operación con éxito, pero no tiene nada que devolver, como un mapa vacío {}o una matriz vacía [], preferiría responder con el código de respuesta 204, aquí hay un extracto de la especificación de Definiciones de código de estado HTTP :

El servidor ha cumplido la solicitud pero no necesita devolver un cuerpo de entidad, y puede querer devolver metainformación actualizada. La respuesta PUEDE incluir metainformación nueva o actualizada en forma de encabezados de entidad, que si están presentes DEBERÍAN estar asociados con la variante solicitada.

Si el cliente es un agente de usuario, NO DEBE cambiar su vista de documento de la que provocó el envío de la solicitud. Esta respuesta está destinada principalmente a permitir la entrada de acciones para que tengan lugar sin causar un cambio en la vista de documento activa del agente de usuario, aunque DEBE aplicarse cualquier metainformación nueva o actualizada al documento actualmente en la vista activa del agente de usuario.

La respuesta 204 NO DEBE incluir un cuerpo de mensaje y, por lo tanto, siempre termina con la primera línea vacía después de los campos de encabezado.

Esencialmente, recomiendo usar 204 en aplicaciones RESTful sobre HTTP cuando no hay nada que devolver.

David Sergey
fuente
44
Estoy de acuerdo con el comentario de @ avakar sobre otra respuesta aquí. Si el cliente está intentando acceder a / v1 / get / movies / 1, debería devolver 404 si no hay películas identificables por 1. Solo / v1 / get / movies debería devolver 200 incluso si no hay película. Pero 204 no es adecuado porque está destinado a acciones de entrada.
imel96
77
Otro problema con esta solución es que normalmente necesitará un código especial en el cliente: si la respuesta es una lista vacía, se puede analizar como JSON como una respuesta normal. Si la respuesta es un cuerpo vacío, el analizador JSON probablemente se quejará (porque un documento vacío no es JSON válido), por lo que el cliente necesita un código adicional para verificar HTTP 204 y omitir el análisis.
sleske
77
Creo que esto es una lectura errónea de la intención de 204. 204 parece estar destinado no a operaciones que esperaban contenido y no pudo encontrar ninguno, sino más bien a operaciones que tuvieron éxito y no tienen un retorno previsto . De Wikipedia: "El servidor procesó con éxito la solicitud, pero no devuelve ningún contenido. Usualmente se usa como respuesta a una solicitud de eliminación exitosa".
XML
10

Se ha realizado una cantidad razonable de trabajo en la creación de un formato estandarizado de API JSON .

Seguir los principios de esa especificación significa que todos los recursos devueltos deberían ser efectivamente "colecciones" (incluso cuando se incluye un solo recurso). Seguir esto significaría que su llamada a /v1/get/moviesvolvería:

{
    "movies": [
        {"name": "Ghostbusters"},
        {"name": "Indiana Jones"}
    ]
}

Su llamada a /v1/get/books(que devuelve cero recursos) devolvería:

{
    "books": []
}
Tim Blair
fuente
5

Para su ejemplo específico, recomendaría que / v1 / get / books devuelva HTTP 200 con una matriz vacía.

Si estoy leyendo tu publicación correctamente, tu API tiene la intención de recopilar libros. Metafóricamente hablando, tiene una estantería para libros, un estante de DVD para películas y posiblemente otros contenedores que no ha mencionado aquí. Debido a que tiene la intención de recolectar libros, / v1 / get / books es su estantería. Esto significa que hay un recurso válido allí, una lista de libros, que está vacío en su ejemplo específico.

La razón por la que no sugiero devolver HTTP 404 en este caso es que la estantería todavía está allí. No hay ningún libro en este momento, pero sigue siendo una estantería. Si no fuera una estantería, por ejemplo, si la API no tenía la intención de recolectar libros, entonces HTTP 404 sería apropiado. Pero debido a que hay un recurso allí, no debe indicar que no hay uno, lo que hace HTTP 404. Por lo tanto, sostengo que 200 con una matriz vacía (que significa la colección) es más apropiado.

La razón por la que no sugiero devolver HTTP 204 es que esto sugeriría que "Sin contenido" es el estado normal de las cosas: realizar esta acción en este recurso normalmente no devolvería nada. Es por eso que generalmente se usa como respuesta a las solicitudes DELETE, por ejemplo: la naturaleza de la eliminación generalmente significa que no queda nada para devolver. El caso es similar cuando se usa para responder a solicitudes con la familia de encabezados If-Modified: solo quería contenido si el recurso había cambiado, pero no lo ha hecho, por lo que no le daré ningún contenido.

Pero sostengo que para OBTENER una colección vacía pero válida, HTTP 204 no tiene sentido. Si hubiera elementos en la colección, la representación adecuada sería una matriz de esos datos. Por lo tanto, cuando no hay datos allí (pero la colección es válida), la representación adecuada es una matriz vacía.

El más cuchara
fuente
5

Realmente deberías hacer solo una de dos cosas

O bien Devuelve un 200 (OK)código de estado y una matriz vacía en el cuerpo.

O devuelva un 204 (NO CONTENT)código de estado y NO cuerpo de respuesta.

Para mí, la opción 2 me parece más técnicamente correcta y está en línea con los principios REST y HTTP.

Sin embargo, la opción 1 parece más eficiente para el cliente, porque el cliente no necesita lógica adicional para diferenciar entre dos códigos de estado (exitosos). Como sabe que siempre recibirá una matriz, simplemente tiene que verificar si no tiene ninguno, uno o muchos elementos y procesarlos adecuadamente

Vihung
fuente
3

He visto ambos casos en entornos de producción. El que elijas dependerá de quién usará la API. Si quieren saber por qué la lista está vacía o para asegurarse de que la lista está realmente vacía y no se produjeron errores al recuperarla, entonces debe adjuntar un objeto "errores". Si no les importa, vaya devolviendo una lista vacía. Optaría por un segundo enfoque, ya que cubre más necesidades que el primero.

devnull
fuente
3

Lo primero que debe tener en cuenta, ya que está creando una API RESTful, es devolver un código de respuesta apropiado. Y el código de respuesta más apropiado para comunicar que la solicitud se realizó normalmente, pero el recurso solicitado no está disponible en este momento es el venerable 404.

Si diseña su API de tal manera que siempre devuelva un código de respuesta razonable, es posible que ni siquiera necesite devolver un cuerpo cuando no se encontró el recurso. Dicho esto, devolver un cuerpo, especialmente uno legible por humanos, no puede doler.

Aquí no hay "mejores prácticas", sus dos ejemplos son arbitrarios, simplemente elija uno y sea ​​coherente . Los desarrolladores odian las sorpresas, si /v1/get/moviesregresa {}cuando no hay películas, /v1/get/actorstambién esperaríamos regresar {}cuando no hay actores.

Yannis
fuente
1
Devolver un 404 realmente es lo correcto, pero lamentablemente nadie realmente lo hace, incluido yo mismo.
RibaldEddie
1
Si tiene respuestas complejas, y solo algunas de ellas están vacías, devolver un 404 confundirá al usuario.
devnull
55
No estaría de acuerdo con el mensaje 404. Un 404 que interpretaría como "el recurso no existe" y me preocuparía si me equivoco con mi URL o lo que sea. Si solicito una lista de películas y obtengo un 404, pensaría que no hay un recurso de película en absoluto. Un "204 Sin contenido" puede ser más apropiado.
thorsten müller
8
Ok, la cosa del "no cuerpo" lo mataría. Pero: "La clase de código de estado 4xx está destinada a casos en los que el cliente parece haberse equivocado". Pero no hubo error en el lado del cliente. Entonces, un 404 da información incorrecta. Envíe 204 sin un cuerpo o diga que está bien y envíe una lista vacía.
thorsten müller
99
Estás pidiendo una lista de libros, devolver 404 significaría que la lista no existe, no que está vacía. Devolver 200 junto con una lista vacía me parece la única opción razonable.
avakar
1

No creo que la respuesta correcta sea la que está marcada.

La respuesta proporcionada por nirth debería ser la mejor, en un verdadero escenario REST. La respuesta del cuerpo debe estar vacía y el código de estado http: 204; el recurso existe pero no tiene "contenido" en ese momento: está vacío.

REST HTTP_Status_Codes

jmmtcarvalho
fuente
1

Recomiendo más de 200 matrices vacías, ya que simplifica el manejo por parte de todos los clientes de la API. 200+ array significa "Devolví todos los datos que están allí". Tanto para el código que entrega los datos como para el código que los procesa, el número de elementos sería irrelevante.

Todos los demás códigos de estado deben estar debidamente documentados y entregados por el servidor y procesados ​​por el cliente, y todos sabemos la probabilidad de que esto suceda.

Hubo una sugerencia para devolver el estado 204 + cuerpo vacío. Eso significa que se fuerza cada solo cliente al estado del proceso 204 correctamente. ¡Además, los obligas a manejar respuestas que no sean JSON! Espero que todos se den cuenta de que solo porque una solicitud recibió una respuesta, no significa que la respuesta vino del servidor cuando se usa http, y solo una verificación de que la respuesta es JSON maneja muchos de esos casos.

gnasher729
fuente
0

Yo "depende".

Si cero es un resultado razonable, devuelva la lista vacía. Por ejemplo, si desea obtener todos los empleados llamados "bob" donde "ninguno" es un resultado bastante razonable. Si no es un resultado esperado, devuelve un error. Por ejemplo, obtener una lista histórica de las direcciones de las calles de una persona que usted emplea. Deben vivir en algún lugar, por lo que probablemente ningún resultado sea un error, no solo una condición normal.

Estoy seguro de que puede discutir con los detalles de mi ejemplo, pero tiene la idea ...

JohnB
fuente
0
  • En primer lugar, tener geten su URL no es RESTful, GET está implícito en el método HTTP.
  • Si está solicitando una colección como GET api/moviesreturn a 200 OKcon una matriz vacía [].
  • Si está solicitando una película específica como GET api/movies/1(donde 1está la identificación) y no existe, devuelva a 404 Not Found.

¿Por qué? Estás solicitando recursos . Cuando solicita la colección, el recurso en sí (la colección) existe. Por lo tanto, a 404está mal. Pero si solicita una película específica y no existe, el recurso solicitado no existe y debe devolver a 404.

felixfbecker
fuente
-2

Si está devolviendo JSON, es mejor devolver siempre el mensaje de conteo y error y tal vez un booleano que indique si hay error o no, esos son mis tres meta valores estándar devueltos con cada lista de filas.

sino
fuente