¿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.
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
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).
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).
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.)
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...
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:
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
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
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.
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>
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