restful - ¿Cuáles son las mejores prácticas para los recursos anidados REST?
restful api designing guidelines the best practices (6)
Por lo que puedo decir, cada recurso individual debe tener solo una ruta canónica . Entonces, en el siguiente ejemplo, ¿cuáles serían los buenos patrones de URL?
Tomemos como ejemplo una representación de empresas de descanso. En este ejemplo hipotético, cada compañía posee 0 o más departamentos y cada departamento posee 0 o más empleados.
Un departamento no puede existir sin una empresa asociada.
Un empleado no puede existir sin un departamento asociado.
Ahora encontraría la representación natural de los patrones de recursos.
-
/companies
Una colección de empresas - Acepta poner para una nueva empresa. Obtener por toda la colección. -
/companies/{companyId}
Una empresa individual. Acepta GET, PUT y DELETE -
/companies/{companyId}/departments
Acepta POST para un nuevo artículo. (Crea un departamento dentro de la empresa.) -
/companies/{companyId}/departments/{departmentId}/
-
/companies/{companyId}/departments/{departmentId}/employees
-
/companies/{companyId}/departments/{departmentId}/employees/{empId}
Dadas las restricciones, en cada una de las secciones, siento que esto tiene sentido si está un poco profundamente anidado.
Sin embargo, mi dificultad viene si quiero enumerar ( GET
) a todos los empleados de todas las compañías.
El patrón de recursos para eso se asignaría más estrechamente a /employees
(La recopilación de todos los empleados)
¿Eso significa que debo tener /employees/{empId}
también porque si es así, hay dos URI de conseguir el mismo recurso?
O tal vez todo el esquema debería ser aplanado, pero eso significaría que los empleados son un objeto anidado de nivel superior.
En un nivel básico /employees/?company={companyId}&department={deptId}
devuelve exactamente la misma vista de empleados como el patrón más profundamente anidado.
¿Cuál es la mejor práctica para los patrones de URL donde los recursos son propiedad de otros recursos pero deberían poder consultarse por separado?
ACTUALIZACIÓN: ver mi respuesta a continuación para ver lo que he hecho.
El aspecto de tus URL no tiene nada que ver con REST. Todo vale. En realidad es un "detalle de implementación". Así que al igual que cómo nombras tus variables. Todo lo que tienen que ser es único y duradero.
No pierda demasiado tiempo en esto, solo elija y apéguese a él / sea consistente. Por ejemplo, si va con jerarquías, entonces lo hace para todos sus recursos. Si va con parámetros de consulta, etc., como las convenciones de nomenclatura en su código.
Porque ? Por lo que sé, una API "RESTful" debe ser navegable (ya sabes ... "Hipermedia como el motor del estado de la aplicación"), por lo tanto, a un cliente API no le importa cómo son tus URL siempre y cuando sean válido (no hay SEO, no hay humanos que necesiten leer esas "urls amigables", excepto que pueden ser para depurar ...)
Lo agradable / comprensible que es una URL en una API REST solo es interesante para usted como desarrollador de API, no como cliente API, como lo sería el nombre de una variable en su código.
Lo más importante es que su cliente API sepa cómo interpretar su tipo de medio. Por ejemplo sabe que:
- su tipo de medio tiene una propiedad de enlaces que enumera enlaces disponibles / relacionados.
- Cada enlace se identifica mediante una relación (al igual que los navegadores saben que el enlace [rel = "hoja de estilo"] significa que es una hoja de estilo o rel = favico es un enlace a un favicon ...)
- y sabe lo que significan esas relaciones ("compañías" significa una lista de compañías, "búsqueda" significa una url programada para hacer una búsqueda en una lista de recursos, "departamentos" significa departamentos del recurso actual)
A continuación se muestra un ejemplo de intercambio HTTP (los cuerpos están en yaml ya que es más fácil de escribir):
Solicitud
GET / HTTP/1.1
Host: api.acme.io
Accept: text/yaml, text/acme-mediatype+yaml
Respuesta: una lista de enlaces al recurso principal (empresas, personas, lo que sea ...)
HTTP/1.1 200 OK
Date: Tue, 05 Apr 2016 15:04:00 GMT
Last-Modified: Tue, 05 Apr 2016 00:00:00 GMT
Content-Type: text/acme-mediatype+yaml
# body: this is your API''s entrypoint (like a homepage)
links:
# could be some random path https://api.acme.local/modskmklmkdsml
# the only thing the API client cares about is the key (or rel) "companies"
companies: https://api.acme.local/companies
people: https://api.acme.local/people
Solicitud: enlace a empresas (utilizando body.links.companies de respuestas anteriores)
GET /companies HTTP/1.1
Host: api.acme.local
Accept: text/yaml, text/acme-mediatype+yaml
Respuesta: una lista parcial de compañías (en los ítems), el recurso contiene enlaces relacionados, como el enlace para obtener las siguientes empresas (body.links.next) y otro enlace (con plantilla) para buscar (body.links.search)
HTTP/1.1 200 OK
Date: Tue, 05 Apr 2016 15:06:00 GMT
Last-Modified: Tue, 05 Apr 2016 00:00:00 GMT
Content-Type: text/acme-mediatype+yaml
# body: representation of a list of companies
links:
# link to the next page
next: https://api.acme.local/companies?page=2
# templated link for search
search: https://api.acme.local/companies?query={query}
# you could provide available actions related to this resource
actions:
add:
href: https://api.acme.local/companies
method: POST
items:
- name: company1
links:
self: https://api.acme.local/companies/8er13eo
# and here is the link to departments
# again the client only cares about the key department
department: https://api.acme.local/companies/8er13eo/departments
- name: company2
links:
self: https://api.acme.local/companies/9r13d4l
# or could be in some other location !
department: https://api2.acme.local/departments?company=8er13eo
Entonces, como verás si vas por los enlaces / relaciones, la forma en que estructuras la parte de la ruta de tus URL no tiene ningún valor para tu cliente API. Y si está comunicando la estructura de sus URL a su cliente como documentación, entonces no está haciendo REST (o al menos no el Nivel 3 según el " Modelo de madurez de Richardson ")
He leído todas las respuestas anteriores, pero parece que no tienen una estrategia común. Encontré un buen artículo sobre las mejores prácticas en Design API de Microsoft Documents . Creo que deberías referirte.
En sistemas más complejos, puede ser tentador proporcionar URI que permitan a un cliente navegar a través de varios niveles de relaciones, tales como
/customers/1/orders/99/products.
Sin embargo, este nivel de complejidad puede ser difícil de mantener y es inflexible si las relaciones entre los recursos cambian en el futuro. En su lugar, trate de mantener las URI relativamente simples . Una vez que una aplicación tiene una referencia a un recurso, debería ser posible utilizar esta referencia para encontrar elementos relacionados con ese recurso. La consulta anterior se puede reemplazar con el URI/customers/1/orders
para encontrar todos los pedidos para el cliente 1, y luego/orders/99/products
para encontrar los productos en este orden.
.
Propina
Evite requerir URI de recursos más complejos que la
collection/item/collection
.
He probado ambas estrategias de diseño: puntos finales anidados y no anidados. He encontrado que
Si el recurso anidado tiene una clave principal y usted no tiene su clave primaria principal, la estructura anidada requiere que la obtenga, aunque el sistema no la requiera.
Los puntos finales anidados normalmente requieren puntos finales redundantes. En otras palabras, usted más a menudo que no, necesitan los empleados adicionales / de punto final para que pueda obtener una lista de los empleados en todos los departamentos. Si tiene / empleados, ¿qué es exactamente lo que compran / empresas / departamentos / empleados?
Los puntos finales de anidación no evolucionan tan bien. Por ejemplo, es posible que no necesite buscar empleados ahora, pero puede hacerlo más tarde y si tiene una estructura anidada, no tiene más remedio que agregar otro punto final. Con un diseño no anidado, solo agrega más parámetros, que es más simple.
A veces un recurso puede tener múltiples tipos de padres. Resultando en múltiples puntos finales, todos devolviendo el mismo recurso.
los puntos finales redundantes hacen que la documentación sea más difícil de escribir y también hace que la API sea más difícil de aprender.
En resumen, el diseño no anidado parece permitir un esquema de punto final más flexible y más simple.
He trasladado lo que he hecho de la pregunta a una respuesta donde es probable que más personas lo vean.
Lo que he hecho es tener los puntos finales de creación en el punto final anidado. El punto final canónico para modificar o consultar un elemento no está en el recurso anidado .
Entonces, en este ejemplo (solo listando los puntos finales que cambian un recurso)
-
POST
/companies/
crea una nueva compañía devuelve un enlace a la compañía creada. -
POST
/companies/{companyId}/departments
cuando se coloca un departamento crea el nuevo departamento devuelve un enlace a/departments/{departmentId}
-
PUT
/departments/{departmentId}
modifica un departamento -
POST
/departments/{deparmentId}/employees
crea un nuevo empleado devuelve un enlace a/employees/{employeeId}
Así que hay recursos de nivel raíz para cada una de las colecciones. Sin embargo, la creación está en el objeto propietario .
Lo que has hecho es correcto. En general, puede haber muchos URI en el mismo recurso, no hay reglas que indiquen que no debería hacerlo.
Y, en general, es posible que necesite acceder a los elementos directamente o como un subconjunto de otra cosa, para que su estructura tenga sentido para mí.
Solo porque los empleados son accesibles bajo departamento:
company/{companyid}/department/{departmentid}/employees
No significa que no puedan ser accesibles bajo compañía también:
company/{companyid}/employees
Lo que devolvería empleados para esa empresa. Depende de lo que necesite su cliente consumidor, para eso debe diseñar.
Pero espero que todos los manipuladores de URLs utilizan el mismo código respaldo para satisfacer las peticiones de manera que no está duplicando código.
No estoy de acuerdo con este tipo de camino
GET /companies/{companyId}/departments
Si desea obtener departamentos, creo que es mejor usar un recurso de / department
GET /departments?companyId=123
Supongo que tiene una tabla de companies
y una tabla de departments
luego clases para mapearlas en el lenguaje de programación que usa. También asumo que los departamentos podrían asociarse a otras entidades que no sean empresas, por lo que un recurso / department es sencillo, es conveniente tener recursos asignados a tablas y tampoco necesita tantos puntos finales ya que puede reutilizarlos
GET /departments?companyId=123
para cualquier tipo de búsqueda, por ejemplo
GET /departments?name=xxx
GET /departments?companyId=123&name=xxx
etc.
Si quieres crear un departamento, el
POST /departments
se debe utilizar el recurso y el cuerpo de la solicitud debe contener el ID de la empresa (si el departamento puede estar vinculado a una sola compañía).