web-dev-qa-db-fra.com

Comment les liens de style HATEOAS doivent-ils être implémentés pour les collections RESTful JSON?

Pour garder les choses simples et éviter les collisions de noms, j'ai regroupé des liens dans mes ressources d'enregistrement comme ceci ...

{
    id: 211,
    first_name: 'John',
    last_name: 'Lock',
    _links: [
        { rel: 'self', href: 'htttp://example.com/people/211' }
    ]
}

Cependant, je ne peux pas trouver comment implémenter des liens dans les collections. J'ai passé beaucoup de temps à naviguer sur le Web pour des exemples et à part utiliser le pas si maigre HAL Je ne peux pas concilier mon problème.

[
    {id:1,first_name:.....},
    {id:2,first_name:.....},
    {id:3,first_name:.....}, 
    "_links": "Cant put a key value pair here because its an-array" 
]

Ce qui signifie que je dois envelopper le tableau dans un objet conteneur.

[
    people: [ {id:1,first_name:.....} ],
    links: [ { rel:parent, href:.... ]
]

Mais c'est différent de la ressource singulière, donc je vais faire en sorte que le disque se comporte comme la collection et l'envelopper dans un conteneur ...

{
    person: {
        id: 211,
        first_name: 'John',
        last_name: 'Lock',
        _links: 
    },
    links:[
        { rel: 'self', href: 'htttp://example.com/people/211' }
    ] 
}

En surface, cela semble être une solution assez soignée. Le JSON résultant est un niveau plus profond mais HATEOAS a été implémenté, donc ça va? Pas du tout. La vraie piqûre vient quand je reviens à la collection. Maintenant que la ressource unique a été enveloppée dans un conteneur afin d'être cohérente avec la collection, la collection doit maintenant être modifiée afin de refléter les changements. Et c'est là que ça devient moche. Très laid. Maintenant, la collection ressemble à ceci ...

{
    "people": [
        {
            "person": {
                ....
            },
            "links" : [
                {
                    "rel": "self",
                    "href": "http://example.com/people/1"
                }
            ]
        },
        {
            "person": {
                ....
            },
            "links" : [
                {
                    "rel": "self",
                    "href": "http://example.com/people/2"
                }
            ]
        }
    ],
    "links" : [
        {
            "rel": "self",
            "href": "http://example.com/people"
        }
    ]
}

Existe-t-il une solution plus simple pour implémenter HATEOAS pour les collections? Ou devrais-je dire au revoir à HATEOAS pour m'avoir forcé à trop compliquer la structure des données?

35
thomas-peter

Veuillez ne pas rejeter HAL si rapidement juste parce qu'il a l'air un peu gonflé (dans sa forme JSON, c'est assez minime).

HAL est pour JSON ce que HTML est pour du texte brut.

Il ajoute des hyperliens. Vous avez besoin d'hyperliens et d'un format de représentation généralement compris (comme HAL ou Collection + JSON) pour REST. Vous avez également besoin de HATEOAS pour REST, sans HATEOAS ce n'est pas REST! HATEOAS nécessite bien sûr des hyperliens.

Dans votre cas, vous essayez de créer une ressource de collection. La relation enregistrée par l'IANA pour cela est "item" (avec relation inverse "collection"). Voici la représentation dans HAL pour une collection People:

{
    "_links": {
        "self": { "href": "http://example.com/people" },
        "item": [
            { "href": "http://example.com/people/1", "title": "John Smith" },
            { "href": "http://example.com/people/2", "title": "Jane Smith" }
        ]
    },
    "_embedded": {
        "http://example.com/rels#person": [
            {
                "first_name": "John",
                "last_name": "Smith",
                "_links": {
                    "self": { "href": "http://example.com/people/1" },
                    "http://example.com/rels#spouse": { "href": "http://example.com/people/2" }
                }
            },
            {
                "first_name": "Jane",
                "last_name": "Smith",
                "_links": {
                    "self": { "href": "http://example.com/people/2" },
                    "http://example.com/rels#spouse": { "href": "http://example.com/people/1" }
                }
            }
        ]
    }
}

Remarque:

  • Les données principales de cette collection proviennent de _links.item[]. Ce sont les articles de la collection. Les données complètes (ou au moins certaines données supplémentaires) pour chaque élément sont disponibles dans le _embedded tableau. Si le client a besoin de ces données supplémentaires, il doit les trouver en recherchant à travers _embedded[n]._links.self.href pour chaque n. Il s'agit d'une contrainte de conception de HAL. D'autres formats de représentation hypermédia ont des contraintes similaires (mais vont peut-être dans l'autre sens).

  • J'ai ajouté une valeur title pour chaque membre du tableau item. Cela peut apparaître entre les balises d'ancrage d'ouverture et de fermeture en cas de rendu au format HTML, ou en tant que texte d'un élément de menu dans le client, sans qu'il soit nécessaire de poursuivre le traitement de la représentation par le client.

  • Il n'y a pas de paramètres ID. Toutes les références à d'autres ressources sont exposées sous forme de liens hypertexte. Un client ne devrait pas avoir à "construire" une URL en collant un ID dans une URL à un endroit prédéfini. Il s'agit d'informations hors bande qui empêchent les modifications indépendantes du client et du serveur.

  • Tous vos hyperliens doivent être absolus, car les URL relatives peuvent provoquer des problèmes. Toutes vos relations doivent être répertoriées sur cette page IANA ou utiliser un URI pour les définir. Idéalement, cet URI devrait être une URL HTTP déréférençable avec une documentation sur la relation à l'autre extrémité.

35
Nicholas Shanks

Il semble que la liaison JSON ne soit pas encore réglée. Il y a plusieurs prétendants:

Les références

15
Willie Wheeler

Tout d'abord, je ne crois pas que les API qui ont des points de terminaison qui retournent des collections (tableaux JSON) soient vraiment RESTful. Cependant, la plupart des API "REST" détournent les règles ici.

J'ai récemment développé une REST pour le flux XML NextBus appelée restbus qui renvoie les collections de certains points d'extrémité tout en utilisant des liens hypertextes de style HATEOAS. Ici est un échantillon de la structure que j'ai utilisée:

{
  // ... SF-Muni resource from restbus API ...

  _links: {
    self: {
      href: "http://localhost:3535/agencies/sf-muni",
      type: "application/json",
      rel: "self",
      rt: "agency",
      title: "Transit agency 'sf-muni'."
    },
    to: [
      {
        href: "http://localhost:3535/agencies/sf-muni/routes",
        type: "application/json",
        rel: "describedby",
        rt: "route",
        title: "A collection of routes for transit agency 'sf-muni'."
      },
      {
        href: "http://localhost:3535/agencies/sf-muni/vehicles",
        type: "application/json",
        rel: "describedby",
        rt: "vehicle",
        title: "A collection of vehicles for transit agency 'sf-muni'."
      }
    ],
    from: [
      {
        href: "http://localhost:3535/agencies",
        type: "application/json",
        rel: "bookmark",
        rt: "agency",
        title: "A collection of transit agencies. This is the API root!"
      }
    ]
  }

}

Il n'essaie pas de suivre les stratégies de liaison JSON populaires (ou leurs types de médias associés) comme HAL etal . car ils ne semblent pas être sur le IETF Standards Track (pour l'instant). Au lieu de cela, les attributs de cible d'objet de lien et les relations de lien se rencontrent Spécifications de la liaison Web RFC 5988 autant que possible.

Vous pouvez voir plus de détails sur la structure de lien hypertexte restbus .

3
Morgan

Vous pouvez essayer de regarder spécification d'objet reposant . Que les gars créent une API concrète. Comme je n'aime pas l'idée, il existe de nombreuses solutions pratiques que vous pouvez en tirer.

1
swist