¿Cuál es el mejor método REST para devolver la cantidad total de elementos en un objeto?

Estoy desarrollando un servicio REST API para un gran sitio web de redes sociales en el que participo. Hasta ahora, está funcionando muy bien. Puedo emitir POST GET , POST , PUT y DELETE para objetar URL y afectar mis datos. Sin embargo, esta información está paginada (limitada a 30 resultados a la vez).

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

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

  • / api / members -Returns una lista de miembros (30 a la vez como se mencionó anteriormente)
  • / api / members / 1 -Afecta a un único miembro, según el método de solicitud utilizado

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

Aunque la respuesta a / API / users está paginada 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 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 de los usuarios: https://api.stackexchange.com/docs/users

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

Para el número total de elementos, utilizo el encabezado X-total-count .
Para enlaces a la página siguiente, anterior, etc. Uso el 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 devuelves contenido que no admite hipervínculos (es decir, binarios, imágenes).

He estado haciendo una investigación exhaustiva sobre esta y otras preguntas relacionadas con la búsqueda de REST últimamente y me pareció constructivo agregar algunos de mis hallazgos aquí. Estoy expandiendo la pregunta un poco para incluir pensamientos sobre la búsqueda y el recuento, ya que están íntimamente relacionados.

Encabezados

Los metadatos de búsqueda se incluyen en la respuesta en forma de encabezados de respuesta. El gran beneficio de este enfoque es que la carga de respuesta en sí misma es solo el solicitante de datos real que estaba solicitando. Facilita el procesamiento de la respuesta a los clientes que no están interesados ​​en la información de búsqueda.

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

X-Total-Count

 X-Total-Count: 234 

Esto se usa en algunas API que encontré en la naturaleza. También hay paquetes de NPM para agregar soporte para este encabezado, por ejemplo, Loopback. Algunos artículos recomiendan configurar este encabezado también.

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

Enlazar

 Link: ; rel="previous"; title*=UTF-8'de'letztes%20Kapitel, ; rel="next"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel 

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

Alternativamente, algunas API y, por ejemplo, el estándar JsonApi , utilizan el formato de 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 del recuento total) a expensas de una mayor complejidad para acceder a los datos reales en sí (al agregar un sobre).

Rango de contenido

 Content-Range: items 0-49/234 

Promovido por un artículo de blog llamado encabezado Rango, ¡te elijo (para la paginación)! . El autor hace una buena defensa del uso de los encabezados Range y Content-Range para la paginación. Cuando leemos cuidadosamente el RFC en estos encabezados, encontramos que la extensión de su significado más allá de los rangos de bytes en realidad fue anticipada por el RFC y está explícitamente permitida. Cuando se utiliza en el contexto de items lugar de bytes , el encabezado del rango en realidad nos da una manera de solicitar un cierto rango de ítems e indicar qué rango del resultado total se relaciona con los ítems de respuesta. Este encabezado también ofrece una excelente manera de mostrar el recuento total. Y es un verdadero estándar que principalmente mapea de uno en uno a paginación. También se usa en la naturaleza .

Sobre

Muchas API, incluida la de nuestro sitio web favorito de preguntas y respuestas, usan un sobre , un envoltorio alrededor de los datos que se utiliza para agregar metainformación sobre los datos. Además, los estándares OData y JsonApi usan un sobre de respuesta.

La gran desventaja de esto (imho) es que el procesamiento de los datos de respuesta se vuelve más complejo ya que los datos reales deben encontrarse en algún lugar de la envolvente. 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.

Punto final separado

Creo que esto se ha cubierto lo suficiente en las otras respuestas. No investigué tanto porque estoy de acuerdo con los comentarios de que esto es confuso ya que ahora tiene múltiples tipos de endpoints. Creo que es mejor si cada punto final representa un (recurso de) recurso (s).

Pensamientos adicionales

No solo tenemos que comunicar la metainformación de paginación relacionada con la respuesta, sino también permitir que el cliente solicite páginas / rangos específicos. Es interesante mirar también 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 una amplia gama de parámetros de solicitud utilizados con frecuencia, como pagesize , page[size] y limit etc. además de soportar el encabezado del Range (y como parámetro de solicitud también).

Podría devolver el conteo como un encabezado HTTP personalizado en respuesta a una solicitud HEAD. De esta forma, si un cliente solo quiere el recuento, no es necesario que devuelva la lista real, y no hay necesidad de una URL adicional.

(O bien, si se encuentra en un entorno controlado desde el punto final hasta el punto final, puede usar un verbo HTTP personalizado como COUNT).

Alternativa cuando no necesita elementos reales

La respuesta de Franci Penov es sin duda la mejor manera de hacerlo, por lo que siempre devuelve los artículos junto con todos los metadatos adicionales sobre las entidades solicitadas. Esa es la forma en que debería hacerse.

pero a veces devolver todos los datos no tiene sentido, porque puede que no los necesites en absoluto. Quizás todo lo que necesitas es metadatos sobre tu recurso solicitado. Como el conteo total o el número de páginas o algo más. En tal caso, siempre puede hacer que la consulta URL indique a su servicio que no devuelva elementos, sino solo metadatos como:

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

o algo similar…

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

¿Qué pasa con un nuevo punto final> / api / members / count que simplemente llama a Members.Count () y devuelve el resultado?

Parece más fácil simplemente agregar un

 GET /api/members/count 

y devolver el recuento total de miembros

A veces, los marcos (como $ resource / AngularJS) requieren una matriz como resultado de la consulta, y realmente no se puede tener una respuesta como {count:10,items:[...]} en este caso, yo guardo “count” en responseHeaders .

PD En realidad, puedes hacer eso con $ resource / AngularJS, pero necesita algunos ajustes.

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, para que sepa si obtuvo todos los datos en respuesta o no. Cuando hay menos datos en respuesta que un tamaño de página, entonces tienes datos completos. Cuando se devuelve una página completa, debe volver a solicitar otra página.

Prefiero tener un punto final separado para contar (o el mismo punto final con parámetro countOnly). Debido a que podría preparar al usuario final para el proceso de consumo prolongado y prolongado al mostrar la barra de progreso iniciada correctamente.

Si desea devolver el tamaño del datasize en cada respuesta, debe haber pageSize, offset mentionded también. Para ser sincero, la mejor manera es repetir los filtros de solicitud también. Pero la respuesta se volvió muy compleja. Por lo tanto, prefiero el punto final dedicado para devolver el conteo.

              

Couleage mío, prefiero un parámetro countOnly al punto final existente. Entonces, cuando se especifica, la respuesta contiene solo metadatos.

punto final? filtro = valor

     ...   

punto final? filter = value & countOnly = true