2012-08-28 17 views
54

Quiero admitir la paginación en mi API RESTful.Carga útil de respuesta de paginación de una API RESTful

Mi método API debe devolver una lista JSON del producto a través de /products/index. Sin embargo, hay potencialmente miles de productos, y quiero a la página a través de ellos, por lo que mi solicitud debe ser algo como esto:

/products/index?page_number=5&page_size=20 

Pero lo que necesita mi respuesta JSON para parecerse? ¿Los consumidores de la API típicamente esperarían metadatos de paginación en la respuesta? ¿O solo es necesaria una variedad de productos? ¿Por qué?

Parece que la API de Twitter incluye metadatos: https://dev.twitter.com/docs/api/1/get/lists/members (ver Ejemplo de solicitud).

Con metadatos:

{ 
    "page_number": 5, 
    "page_size": 20, 
    "total_record_count": 521, 
    "records": [ 
    { 
     "id": 1, 
     "name": "Widget #1" 
    }, 
    { 
     "id": 2, 
     "name": "Widget #2" 
    }, 
    { 
     "id": 3, 
     "name": "Widget #3" 
    } 
    ] 
} 

Sólo una gama de productos (no hay datos Meta):

[ 
    { 
    "id": 1, 
    "name": "Widget #1" 
    }, 
    { 
    "id": 2, 
    "name": "Widget #2" 
    }, 
    { 
    "id": 3, 
    "name": "Widget #3" 
    } 
] 

Respuesta

70

Las API ReSTful son consumidas principalmente por otros sistemas, por lo que pongo datos de paginación en los encabezados de respuesta. Sin embargo, algunos consumidores API pueden no tener acceso directo a los encabezados de respuesta, o pueden construir un UX sobre su API, por lo que proporcionar una forma de recuperar (a petición) los metadatos en la respuesta JSON es una ventaja.

Creo que su implementación debe incluir metadatos legibles por máquina de manera predeterminada y metadatos legibles por humanos cuando se soliciten. Los metadatos legibles por humanos se pueden devolver con cada solicitud si lo desea o, preferiblemente, a pedido a través de un parámetro de consulta, como include=metadata o include_metadata=true.

En su situación particular, incluiría el URI para cada producto con el registro. Esto facilita que el consumidor API cree enlaces a los productos individuales. También establecería algunas expectativas razonables según los límites de mis solicitudes de búsqueda.Implementar y documentar configuraciones predeterminadas para el tamaño de página es una práctica aceptable. Por ejemplo, GitHub's API establece el tamaño de página predeterminado en 30 registros con un máximo de 100, además establece un límite de velocidad en la cantidad de veces que puede consultar la API. Si su API tiene un tamaño de página predeterminado, entonces la cadena de consulta solo puede especificar el índice de página.

En el escenario legible por humanos, cuando se navega a /products?page=5&per_page=20&include=metadata, la respuesta podría ser:

{ 
    "_metadata": 
    { 
     "page": 5, 
     "per_page": 20, 
     "page_count": 20, 
     "total_count": 521, 
     "Links": [ 
     {"self": "/products?page=5&per_page=20"}, 
     {"first": "/products?page=0&per_page=20"}, 
     {"previous": "/products?page=4&per_page=20"}, 
     {"next": "/products?page=6&per_page=20"}, 
     {"last": "/products?page=26&per_page=20"}, 
     ] 
    }, 
    "records": [ 
    { 
     "id": 1, 
     "name": "Widget #1", 
     "uri": "/products/1" 
    }, 
    { 
     "id": 2, 
     "name": "Widget #2", 
     "uri": "/products/2" 
    }, 
    { 
     "id": 3, 
     "name": "Widget #3", 
     "uri": "/products/3" 
    } 
    ] 
} 

Por metadatos legibles por máquina, añadiría Link headers a la respuesta:

Link: </products?page=5&perPage=20>;rel=self,</products?page=0&perPage=20>;rel=first,</products?page=4&perPage=20>;rel=previous,</products?page=6&perPage=20>;rel=next,</products?page=26&perPage=20>;rel=last 

(El valor del encabezado del enlace debe ser urlencoded)

... y posiblemente unpersonalizadocabecera de la respuesta, si así lo desea:

total-count: 521 

Los otros datos de localización reveladas en los metadatos humanocentrista podrían ser superfluas para los metadatos máquina centradas, como las cabeceras de enlace que me haga saber qué página estoy encendido y el número por página, y puedo recuperar rápidamente el número de registros en la matriz. Por lo tanto, probablemente solo crearía un encabezado para el recuento total. Siempre puede cambiar de parecer más adelante y agregar más metadatos.

Como nota aparte, puede observar que eliminé /index de su URI. Una convención generalmente aceptada es tener sus colecciones de exposición de puntos finales ReST. Al tener /index al final se ensucia un poco.

Estas son solo algunas cosas que me gusta tener al consumir/crear una API. ¡Espero que ayude!

+0

per_page no sigue la convención page_count –

+1

'" page_count ": 20' y' {"last": "/ products? Page = 26 & per_page = 20"} '? –

+0

¿qué pasaría si el conteo del producto aumenta repentinamente mientras se obtienen todos los registros de la página 1 a la página x? – MeV

23

Como alguien que ha escrito varias bibliotecas para el consumo de los servicios REST, te voy a dar la La perspectiva del cliente sobre por qué creo que el ajuste del resultado en metadatos es el camino a seguir:

  • Sin el recuento total , ¿cómo puede saber el cliente que aún no ha recibido todo lo que hay y debe seguir buscando en el conjunto de resultados? En una interfaz de usuario que no funciona mira hacia adelante a la siguiente página, en el peor de los casos esto podría representarse como un enlace Siguiente/Más que en realidad no obtuvo más datos.
  • La inclusión de metadatos en la respuesta permite al cliente rastrear menos estados. Ahora no tengo que hacer coincidir mi solicitud REST con la respuesta, ya que la respuesta contiene los metadatos necesarios para reconstruir el estado de solicitud (en este caso, el cursor en el conjunto de datos).
  • Si el estado es parte de la respuesta, puedo realizar varias solicitudes en el mismo conjunto de datos simultáneamente, y puedo manejar las solicitudes en cualquier orden en que lleguen, que no es necesariamente el orden en el que hice las solicitudes.

Y una sugerencia: como el Twitter API, debe reemplazar el número de página con un índice/cursor recto. La razón es que la API permite al cliente establecer el tamaño de la página por solicitud. ¿El número de página devuelto es el número de páginas que el cliente ha solicitado hasta el momento, o el número de la página que recibió el último tamaño de página utilizado (casi con certeza el último, pero por qué no evitar esa ambigüedad por completo)?

+5

Para su primera bala, ¿sería una solución adecuada para omitir un rel = próxima enlace si no hay una página siguiente? Para su segundo punto, la información todavía está disponible en la respuesta al cliente, simplemente no está en el cuerpo de la respuesta, sino en los encabezados. +1 en tu último párrafo. –

5

Recomendaría agregar encabezados para el mismo. Mover los metadatos a los encabezados ayuda a deshacerse de los sobres como result, data o records y el cuerpo de la respuesta solo contiene los datos que necesitamos. Puede usar el encabezado Link si también genera enlaces de paginación.

HTTP/1.1 200 
    X-Pagination-Count: 100 
    X-Pagination-Page: 5 
    X-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 se refieren a:

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

Para archivo arrogancia:

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

Cuestiones relacionadas