Cuáles son las mejores prácticas para los recursos anidados en REPOSO


Por lo que puedo decir, cada recurso individual debería tener solo un camino canónico. Entonces, en el siguiente ejemplo, ¿cuáles serían los buenos patrones de URL?

Tomemos como ejemplo una representación rest de Empresas. En este ejemplo hipotético, cada empresa 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 exist sin un departamento asociado.

Ahora me gustaría encontrar la representación natural de los patrones de recursos a ser.

  • /companies A collection of companies - Accepts put for a new company. Obtener para toda la colección.
  • /companies/{companyId} Una empresa individual. Acepta GET, PUT y DELETE
  • /companies/{companyId}/departments Acepta POST para un nuevo ítem. (Crea un departamento dentro del 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) todos los empleados de todas las empresas.

El patrón de recursos para eso se mapearía más estrechamente a /employees (La colección de todos los empleados)

¿Significa eso que debería tener /employees/{empId} también porque si es así, entonces hay dos URI para obtener el mismo recurso?

O tal vez todo el esquema debería ser aplanado, pero eso significaría que los empleados son un objeto de nivel superior anidado.

A un nivel básico /employees/?company={companyId}&department={deptId} devuelve exactamente la misma vista de los empleados que 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 deben ser consultables por separado?


ACTUALIZAR: Ver mi respuesta a continuación para ver lo que he Terminado.

 198
Author: Damaged Organic, 2014-01-06

6 answers

Lo que has hecho es correcto. En general, puede haber muchos URI para el mismo recurso, no hay reglas que digan que no debe hacer eso.

Y en general, es posible que necesite acceder a los elementos directamente o como un subconjunto de otra cosa, por lo que su estructura tiene sentido para mí.

Solo porque los empleados son accesibles en el departamento:

company/{companyid}/department/{departmentid}/employees

No significa que no puedan ser accesibles bajo la compañía también:

company/{companyid}/employees

Que devolvería a los empleados para esa compañía. Depende de lo que necesita su cliente consumidor, eso es para lo que debería estar diseñando.

Pero espero que todos los controladores de URL usen el mismo código de respaldo para satisfacer las solicitudes, de modo que no esté duplicando el código.

 100
Author: jeremyh,
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
2016-08-18 14:21:58

He probado ambas estrategias de diseño: endpoints anidados y no anidados. He encontrado que:

  1. Si el recurso anidado tiene una clave primaria y no tiene su clave primaria principal, la estructura anidada requiere que la obtenga, aunque el sistema en realidad no la requiera.

  2. Los endpoints anidados suelen requerir endpoints redundantes. En otras palabras, la mayoría de las veces necesitará el punto final adicional /employees para obtener una lista de empleados en todos los departamentos. Si tienes / empleados, ¿qué te compra exactamente /empresas/departamentos/empleados?

  3. Los puntos finales de anidamiento no evolucionan tan bien. Por ejemplo, es posible que no necesite buscar empleados ahora, pero puede que más tarde y si tiene una estructura anidada, no tiene más opción que agregar otro punto final. Con un diseño no anidado, solo agrega más parámetros, lo cual es más simple.

  4. A veces, un recurso puede tener varios tipos de padres. Resultando en múltiples todos los endpoints devuelven el mismo recurso.

  5. Los endpoints redundantes hacen que los documentos sean más difíciles de escribir y también 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 simple.

 98
Author: Patc,
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
2016-04-04 19:01:54

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 es no en el recurso anidado.

Así que en este ejemplo (solo enumerando los puntos finales que cambian un recurso)

  • POST /companies/ crea una nueva empresa devuelve un enlace a la empresa.
  • POST /companies/{companyId}/departments cuando se pone 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, el create está en el objeto owning.

 52
Author: Wes,
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-08-29 12:11:44

No estoy de acuerdo con este tipo de camino

GET /companies/{companyId}/departments

Si desea obtener departamentos, creo que es mejor usar un recurso /departments

GET /departments?companyId=123

Supongo que tienes una tabla companies y una tabla departments luego clases para mapearlas en el lenguaje de programación que usas. También asumo que los departamentos podrían estar asociados a otras entidades además de las empresas, por lo que un recurso / departments es sencillo, es conveniente tener recursos asignados a tablas y también no necesita tantos endpoints dado que puede reutilizar

GET /departments?companyId=123

Para cualquier tipo de búsqueda, por ejemplo

GET /departments?name=xxx
GET /departments?companyId=123&name=xxx
etc.

Si desea 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 vincularse a una sola empresa).

 6
Author: Maxime Laval,
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
2016-02-13 18:54:23

El aspecto de tus URLs no tiene nada que ver con el RESTO. Todo vale. En realidad es un "detalle de implementación". Así que al igual que el nombre de sus variables. Todo lo que tienen que ser es único y duradero.

No pierdas demasiado tiempo en esto, solo haz una elección y apégate a ella/sé consistente. Por ejemplo, si vas con jerarquías entonces lo haces para todos tus recursos. Si vas con parámetros de consulta...etc al igual que las convenciones de nombres en su código.

¿Por qué es así ? Por lo que sé un "RESTful" API es ser navegable (ya sabes..."Hypermedia como el Motor del Estado de la Aplicación"), por lo tanto, a un cliente API no le importa cómo son sus URL siempre y cuando sean válidas (no hay SEO, ningún humano que necesite leer esas" url amigables", excepto puede ser para depuración...)

Lo agradable / comprensible que es una URL en una API REST solo es interesante para usted como desarrollador de API, no como cliente de API, como sería el nombre de una variable en su código.

La mayoría lo 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 links que enumera enlaces disponibles/relacionados.
  • Cada enlace se identifica por una relación (al igual que los navegadores saben que link[rel="stylesheet"] significa que es una hoja de estilo o rel=favico es un enlace a un favicon...)
  • y sabe lo que significan esas relaciones ("empresas" significa una lista de empresas, "búsqueda" significa una url templada 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 las empresas (utilizando el cuerpo de la respuesta anterior.vínculos.empresas)

GET /companies HTTP/1.1
Host: api.acme.local
Accept: text/yaml, text/acme-mediatype+yaml

Respuesta: una lista parcial de empresas (bajo ítems), el recurso contiene enlaces relacionados, como enlace para obtener el siguiente par de empresas (cuerpo.vínculos.siguiente) otro enlace (con plantilla)para buscar (cuerpo.vínculos.buscar)

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

Así que como ves si vas por el camino de enlaces/relaciones, la forma en que estructurasla parte de 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 REPOSO (o al menos no Nivel 3 según " El modelo de madurez de Richardson")

 4
Author: redben,
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
2016-08-26 16:11:00

He leído toda la respuesta anterior, pero parece que no tengo una estrategia común. Encontré un buen artículo sobre las mejores prácticas en la API de diseño de Microsoft Documents. Creo que deberías referirte.

En sistemas más complejos, puede ser tentador proporcionar URI que permitir que un cliente navegue a través de varios niveles de relaciones, tales como /customers/1/orders/99/products. Sin embargo, este nivel de la complejidad puede ser difícil de mantener y es inflexible si el las relaciones entre los recursos cambian en el futuro. En su lugar, trate de mantenga los 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 relacionado con ese recurso. La consulta anterior puede ser reemplazada por el URI /customers/1/orders para encontrar todos los pedidos del cliente 1, y entonces /orders/99/products para encontrar los productos en este orden.

.

Tip

Evite requerir URI de recursos más complejos que collection/item/collection.

 4
Author: Long Nguyen,
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-06-19 02:25:36