¿Cómo modelar una API RESTful con herencia?


Tengo una jerarquía de objetos que necesito exponer a través de una API RESTful y no estoy seguro de cómo deben estructurarse mis URL y qué deben devolver. No pude encontrar las mejores prácticas.

Digamos que tengo Perros y Gatos heredando de Animales. Necesito operaciones CRUD en perros y gatos; también quiero poder hacer operaciones en animales en general.

Mi primera idea fue hacer algo como esto:

GET /animals        # get all animals
POST /animals       # create a dog or cat
GET /animals/123    # get animal 123

La cosa es que la colección /animals es ahora "inconsistente", ya que puede regresar y tomar objetos que no tienen exactamente la misma estructura (perros y gatos). ¿Se considera "RESTful" que una colección devuelva objetos que tienen atributos diferentes?

Otra solución sería crear una URL para cada tipo concreto, así:

GET /dogs       # get all dogs
POST /dogs      # create a dog
GET /dogs/123   # get dog 123

GET /cats       # get all cats
POST /cats      # create a cat
GET /cats/123   # get cat 123

Pero ahora la relación entre perros y gatos está perdida. Si uno desea recuperar todos los animales, se deben consultar los recursos para perros y gatos. El número de URLs también aumentará con cada nuevo subtipo animal.

Otra sugerencia fue aumentar la segunda solución añadiendo esto:

GET /animals    # get common attributes of all animals

En este caso, los animales devueltos solo contendrían atributos comunes a todos los animales, dejando caer atributos específicos para perros y gatos. Esto permite recuperar todos los animales, aunque con menos detalles. Cada objeto devuelto podría contener un enlace a la versión detallada y concreta.

¿Algún comentario o sugerencia?

Author: Alpha Hydrae, 2012-06-04

5 answers

Yo sugeriría:

  • Usando solo un URI por recurso
  • Diferenciación entre animales únicamente a nivel de atributo

Configurar varios URI en el mismo recurso nunca es una buena idea porque puede causar confusión y efectos secundarios inesperados. Teniendo en cuenta que, su URI único debe basarse en un esquema genérico como /animals.

El siguiente desafío de lidiar con toda la colección de perros y gatos en el nivel "base" ya está resuelto en virtud del enfoque URI /animals.

El desafío final de tratar con tipos especializados como perros y gatos se puede resolver fácilmente utilizando una combinación de parámetros de consulta y atributos de identificación dentro de su tipo de medio. Por ejemplo:

GET /animals (Accept : application/vnd.vet-services.animals+json)

{
   "animals":[
      {
         "link":"/animals/3424",
         "type":"dog",
         "name":"Rex"
      },
      {
         "link":"/animals/7829",
         "type":"cat",
         "name":"Mittens"
      }
   ]
}
  • GET /animals - consigue todos los perros y gatos, devolvería tanto Rex como Mitones
  • GET /animals?type=dog - consigue todos los perros, solo volvería Rex
  • GET /animals?type=cat - consigue todos los gatos, solo Manoplas

Entonces, al crear o modificar animales, correspondería al llamante especificar el tipo de animal involucrado:

Tipo de medio: application/vnd.vet-services.animal+json

{
   "type":"dog",
   "name":"Fido"
}

La carga útil anterior podría enviarse con una solicitud POST o PUT.

El esquema anterior le da las características básicas similares a la herencia OO a través del DESCANSO, y con la capacidad de agregar más especializaciones (es decir, más tipos de animales) sin cirugía mayor o cualquier cambio en su URI Esquema.

 35
Author: Brian Kelly,
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
2012-06-04 15:13:40

Me gustaría ir para / animales que devuelven una lista de perros y peces y lo que sea:

<animals>
  <animal type="dog">
    <name>Fido</name>
    <fur-color>White</fur-color>
  </animal>
  <animal type="fish">
    <name>Wanda</name>
    <water-type>Salt</water-type>
  </animal>
</animals>

Debería ser fácil implementar un ejemplo JSON similar.

Los clientes siempre pueden confiar en que el elemento "name" esté allí (un atributo común). Pero dependiendo del atributo "type" habrá otros elementos como parte de la representación animal.

No hay nada inherentemente RESTful o intranquilo en devolver tal lista-REST no prescribe ningún formato específico para representación de datos. Todo lo que dice es que los datos deben tener alguna representación y el formato para esa representación se identifica por el tipo de medio (que en HTTP es el encabezado Content-Type).

Piense en sus casos de uso: ¿necesita mostrar una lista de animales mixtos? Bueno, entonces devuelve una lista de datos de animales mezclados. ¿Necesita una lista de perros? Bueno, haz una lista.

Si lo haces /animales?tipo = perro o / perros es irrelevante con respecto al DESCANSO que no prescribir cualquier formato de URL-que se deja como un detalle de implementación fuera del alcance de REST. REST solo indica que los recursos deben tener identificadores, sin importar el formato.

Debe agregar algunos enlaces de hyper media para acercarse a una API RESTful. Por ejemplo, añadiendo referencias a los detalles del animal:

<animals>
  <animal type="dog" href="/animals/123">
    <name>Fido</name>
    <fur-color>White</fur-color>
  </animal>
  <animal type="fish" href="/animals/321">
    <name>Wanda</name>
    <water-type>Salt</water-type>
  </animal>
</animals>

Mediante la adición de hipervínculos multimedia se reduce el acoplamiento cliente / servidor - en el caso anterior se toma la carga de la construcción de URL lejos del cliente y dejar que el servidor decidir cómo construir URLs (que por definición es la única autoridad de).

 4
Author: Jørn Wildt,
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
2012-06-04 15:16:13

Esta pregunta se puede responder mejor con el apoyo de una mejora reciente introducida en la última versión de OpenAPI.

Ha sido posible combinar esquemas usando palabras clave como oneOf, allOf, anyOf y obtener una carga útil de mensaje validada desde el esquema JSON v1.0.

Https://spacetelescope.github.io/understanding-json-schema/reference/combining.html

Sin embargo, en OpenAPI (anteriormente Swagger), la composición de esquemas ha sido mejorada por las palabras clave discriminador (v2.0+) y oneOf (v3.0+) para realmente apoyar el polimorfismo.

Https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#schemaComposition

Su herencia podría ser modelada usando una combinación de oneOf (para elegir uno de los subtipos) y allOf (para combinar el tipo y uno de sus subtipos). A continuación se muestra una definición de ejemplo para el método POST.

paths:
  /animals:
  post:
   requestBody:
    content:
      application/json:
        schema:
          oneOf:
            - $ref: '#/components/schemas/Dog
            - $ref: '#/components/schemas/Cat
            - $ref: '#/components/schemas/Fish
          discriminator:
            propertyName: animal_type
   responses:
     '201':
       description: Created
components:
  schemas:
    Animal:
      type: object
      required:
        - animal_type
        - name
      properties:
        animal_type:
          type: string
        name:
          type: string
      discriminator:
        property_name: animal_type
    Dog:
      allOf:
        - $ref: "#/components/schemas/Animal"
        - type: object
        - properties:
          playsFetch
            type: string
    Cat:
      allOf:
        - $ref: "#/components/schemas/Animal"
        - type: object
        - properties:
          likesToPurr
            type: string
    Fish:
      allOf:
        - $ref: "#/components/schemas/Animal"
        - type: object
        - properties:
          water-type
            type: string
 2
Author: dbaltor,
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-03-12 10:40:19

Pero ahora la relación entre perros y gatos está perdida.

De hecho, pero tenga en cuenta que URI simplemente nunca refleja las relaciones entre objetos.

 1
Author: G. Demecki,
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
2012-06-04 15:42:16

Sé que esta es una vieja pregunta, pero estoy interesado en investigar más problemas sobre un modelo de herencia relajante

Siempre puedo decir que un perro es un animal y una gallina también, pero la gallina hace huevos mientras que el perro es un mamífero, por lo que no puede.]}

OBTENER animales/: animalID / huevos

No es consistente porque indica que todos los subtipos de animales pueden tener huevos (como consecuencia de la sustitución de Liskov). Habría una alternativa si todos los mamíferos responden con ' 0 ' a esto solicitud, pero ¿qué pasa si también habilito un método POST? ¿Debo tener miedo de que mañana haya huevos de perro en mis crepes?

La única manera de manejar estos escenarios es proporcionar un 'super-recurso' que agregue todos los sub-recursos compartidos entre todos los 'recursos derivados' posibles y luego una especialización para cada recurso derivado que lo necesite, al igual que cuando bajamos un objeto a oop

GET / animals/: animalID / sons GET / hens/: animalID / huevos POST / gallinas/: animales / huevos

El inconveniente, aquí, es que alguien podría pasar una identificación de perro para hacer referencia a una instancia de la colección de gallinas, pero el perro no es una gallina, por lo que no sería incorrecto si la respuesta fue 404 o 400 con un mensaje de razón

¿Me equivoco?

 0
Author: sscnapoli1926,
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-07-13 11:39:03