¿Cuál es el mejor método RESTful para devolver el número total de elementos de un objeto?


Estoy desarrollando un servicio REST API para un gran sitio web de redes sociales en el que estoy involucrado. Hasta ahora, está funcionando muy bien. Puedo emitir GET, POST, PUT y DELETE solicitudes para objetar URL y afectar mis datos. Sin embargo, estos datos se paginan (limitado a 30 resultados a la vez).

Sin embargo, ¿cuál sería la mejor manera RESTful de obtener el número total de miembros, por ejemplo, a través de mi API?

Actualmente, emito solicitudes a una estructura de URL como la siguiente:

  • / api / members - Devuelve una lista de miembros (30 a la vez como se mencionó anteriormente)
  • / api/members / 1-Afecta a un solo miembro, dependiendo del método de solicitud utilizado

Mi pregunta es: ¿cómo usaría una estructura de URL similar para obtener el número total de miembros en mi solicitud? Obviamente, solicitar solo el campo id (similar a la API Graph de Facebook) y contar los resultados sería ineficaz dado solo una porción de 30 los resultados solo se devolverían.

Author: Martin Bean, 2010-09-15

10 answers

Mientras que la respuesta a /API/users se pagina y devuelve solo 30 registros, no hay nada que le impida incluir en la respuesta también el número total de registros y otra información relevante, como el tamaño de la página, el número de página/desplazamiento, etc.

La API de StackOverflow es un buen ejemplo de ese mismo diseño. Aquí está la documentación para el método Users - https://api.stackexchange.com/docs/users

 64
Author: Franci Penov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-15 09:10:40

Prefiero usar encabezados HTTP para este tipo de información contextual.

Para el número total de elementos utilizo X-total-count header.
Para enlaces a la página siguiente, anterior, etc. Uso encabezado http Link :
http://www.w3.org/wiki/LinkHeader

Github lo hace de la misma manera: https://developer.github.com/v3/#pagination

En mi opinión, es más limpio, ya que también se puede usar cuando devuelve contenido que no admite hipervínculos (es decir, binarios, imágenes).

 50
Author: Ondrej Bozek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-14 14:54:04

He estado haciendo una extensa investigación sobre esta y otras preguntas relacionadas con la paginación de DESCANSO últimamente y pensé que sería constructivo agregar algunos de mis hallazgos aquí. Estoy ampliando la pregunta un poco para incluir pensamientos sobre paginación, así como el recuento, ya que están íntimamente relacionados.

Encabezados

Los metadatos de paginación se incluyen en la respuesta en forma de encabezados de respuesta. El gran beneficio de este enfoque es que la carga útil de respuesta en sí es solo el el solicitante de datos estaba pidiendo. Facilitar el procesamiento de la respuesta para los clientes que no están interesados en la información de paginación.

Hay un montón de encabezados (estándar y personalizados) utilizados en el comodín para devolver información relacionada con la paginación, incluido el recuento total.

X-Total-Count

X-Total-Count: 234

Esto se usa en algunos APIs Encontré en la naturaleza. También hay paquetes NPM para agregar soporte para este encabezado a, por ejemplo, Loopback. Algunos los artículos recomiendan establecer este encabezado también.

A menudo se usa en combinación con el encabezado Link, que es una solución bastante buena para la paginación, pero carece de la información de conteo total.

Enlace

Link: </TheBook/chapter2>;
      rel="previous"; title*=UTF-8'de'letztes%20Kapitel,
      </TheBook/chapter4>;
      rel="next"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel

Siento, por leer mucho sobre este tema, que el consenso general es utilizar el Link header para proporcionar enlaces de paginación a los clientes que utilizan rel=next, rel=previous etc. El problema con esto es que carece de la información de cuántos total hay registros, por lo que muchas API combinan esto con el encabezado X-Total-Count.

Alternativamente, algunas API y, por ejemplo, el estándar JsonApi, usan el formato Link, pero agregan la información en un sobre de respuesta en lugar de en un encabezado. Esto simplifica el acceso a los metadatos (y crea un lugar para agregar la información de conteo total) a expensas de la creciente complejidad de acceder a los datos reales en sí (al agregar un envolvente).

Rango de contenido

Content-Range: items 0-49/234

Promovido por un artículo de blog llamado Encabezado de rango, Te elijo (para la paginación)!. El autor hace un caso fuerte para usar los encabezados Range y Content-Range para la paginación. Cuando leemos cuidadosamente el RFC en estos encabezados, encontramos que extender su significado más allá de los rangos de bytes fue realmente anticipado por el RFC y está explícitamente permitido. Cuando se utiliza en el contexto de items en lugar de bytes, el Rango header realmente nos da una manera de solicitar un cierto rango de elementos e indicar a qué rango del resultado total se relacionan los elementos de respuesta. Este encabezado también da una gran manera de mostrar el recuento total. Y es un verdadero estándar que en su mayoría asigna uno a uno a la paginación. También es utilizado en la naturaleza.

Sobre

Muchas API, incluyendo la de nuestro sitio web favorito de preguntas y respuestas usan un sobre , un envoltorio alrededor de los datos que se usa para agregar meta información sobre los datos. Además, los estándares OData y JsonApi ambos usan una envolvente de respuesta.

La gran desventaja de esto (en mi humilde opinión) es que el procesamiento de los datos de respuesta se vuelve más complejo, ya que los datos reales tienen que encontrarse en algún lugar del sobre. También hay muchos formatos diferentes para ese sobre y tienes que usar el correcto. Es revelador que los sobres de respuesta de OData y JsonApi son muy diferentes, con OData mezclando metadatos en múltiples puntos en la respuesta.

Variable separada

Creo que esto ha sido cubierto suficientemente en las otras respuestas. No investigué tanto porque estoy de acuerdo con los comentarios de que esto es confuso, ya que ahora tiene varios tipos de puntos finales. Creo que es mejor si cada punto final representa una (colección de) recurso(s).

Otros pensamientos

No solo tenemos que comunicar la meta información de paginación relacionada con la respuesta, sino también permitir cliente para solicitar páginas/rangos específicos. Es interesante también mirar este aspecto para terminar con una solución coherente. Aquí también podemos usar encabezados (el encabezado Range parece muy adecuado), u otros mecanismos como los parámetros de consulta. Algunas personas abogan por tratar las páginas de resultados como recursos separados, lo que puede tener sentido en algunos casos de uso (por ejemplo, /books/231/pages/52. Terminé seleccionando un rango salvaje de parámetros de solicitud de uso frecuente, como pagesize, page[size] y limit etc además de apoyar el encabezado Range (y también como parámetro de solicitud).

 31
Author: Stijn de Witt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-14 20:56:14

Puede devolver el recuento como un encabezado HTTP personalizado en respuesta a una solicitud de HEAD. De esta manera, si un cliente solo quiere el conteo, no necesita devolver la lista real, y no hay necesidad de una URL adicional.

(O, si se encuentra en un entorno controlado de extremo a extremo, podría usar un verbo HTTP personalizado como COUNT.)

 22
Author: bzlm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-09-15 08:57:07

Alternativa cuando no necesita elementos reales

La respuesta de Franci Penov es sin duda la mejor manera de hacerlo, por lo que siempre devuelve artículos junto con todos los metadatos adicionales sobre sus entidades que se solicitan. Así es como debe hacerse.

Pero a veces devolver todos los datos no tiene sentido, porque es posible que no los necesite en absoluto. Tal vez todo lo que necesita es que los metadatos sobre el recurso solicitado. Como el recuento total o el número de páginas o algo más. En tal caso siempre puede hacer que la consulta URL le diga a su servicio que no devuelva elementos, sino solo metadatos como:

/api/members?metaonly=true
/api/members?includeitems=0

O algo similar...

 21
Author: Robert Koritnik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:55:04

Recomendaría agregar encabezados para lo mismo, como:

HTTP/1.1 200

Pagination-Count: 100
Pagination-Page: 5
Pagination-Limit: 20
Content-Type: application/json

[
  {
    "id": 10,
    "name": "shirt",
    "color": "red",
    "price": "$23"
  },
  {
    "id": 11,
    "name": "shirt",
    "color": "blue",
    "price": "$25"
  }
]

Para más detalles, consulte:

Https://github.com/adnan-kamili/rest-api-response-format

Para el archivo swagger:

Https://github.com/adnan-kamili/swagger-response-template

 11
Author: adnan kamili,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-07 05:26:45

Qué pasa con un nuevo punto final > /api/members/count que solo llama a los Miembros.Count () y devuelve el resultado

 2
Author: Steve Woods,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-09-15 08:45:20

Parece más fácil añadir un

GET
/api/members/count

Y devuelve el total de miembros

 2
Author: willcodejavaforfood,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-09-15 08:46:15

A veces los frameworks (como $resource/AngularJS) requieren una matriz como resultado de una consulta, y realmente no puede tener una respuesta como {count:10,items:[...]} en este caso almaceno "count" en responseHeaders.

P. S. En realidad se puede hacer eso con resource resource/AngularJS, pero necesita algunos ajustes.

 2
Author: Vahe Hovhannisyan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-15 22:40:00

Al solicitar datos paginados, usted sabe (por valor de parámetro de tamaño de página explícito o valor de tamaño de página predeterminado) el tamaño de página, por lo que sabe si recibió todos los datos en respuesta o no. Cuando hay menos datos en respuesta que el tamaño de una página, entonces tienes datos completos. Cuando se devuelve una página completa, tienes que volver a pedir otra página.

Prefiero tener un punto final separado para count (o el mismo punto final con el parámetro countOnly). Porque usted podría preparar al usuario final para el proceso largo / que consume tiempo mostrando la barra de progreso correctamente iniciada.

Si desea devolver datasize en cada respuesta, debe haber pageSize, offset mentioned también. Para ser honesto, la mejor manera es repetir una solicitud de filtros también. Pero la respuesta se volvió muy compleja. Por lo tanto, prefiero punto final dedicado a volver cuenta.

<data>
  <originalRequest>
    <filter/>
    <filter/>
  </originalReqeust>
  <totalRecordCount/>
  <pageSize/>
  <offset/>
  <list>
     <item/>
     <item/>
  </list>
</data>

Couleage mío, prefiera un parámetro countOnly al punto final existente. Por lo tanto, cuando se especifica la respuesta contiene metadatos solo.

Extremo?filter=value

<data>
  <count/>
  <list>
    <item/>
    ...
  </list>
</data>

Extremo?filter = value & countOnly=true

<data>
  <count/>
  <!-- empty list -->
  <list/>
</data>
 -1
Author: Wooff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-18 10:20:19