BT

Diffuser les Connaissances et l'Innovation dans le Développement Logiciel d'Entreprise

Contribuez

Sujets

Sélectionner votre région

Accueil InfoQ Articles Ajouter de la flexibilité à votre implémentation REST avec Yoga

Ajouter de la flexibilité à votre implémentation REST avec Yoga

Favoris

Les APIs REST sont très attractives de par l'élégance de leur conception. Vous obtenez quelque chose qu'Adam Bosworth de Google décrit comme "simple, décontracté, souplement extensible", mais vous n'obtenez pas quelque chose de construit pour des performances constantes.

Les frameworks REST existants font un excellent travail de conversion de votre modèle de domaine en une réponse JSON ou XML. Cependant, ils opèrent en supposant que chaque ressource ne peut être représentée que d'une seule manière. Chaque requête pour une ressource retourne le document en entier, même si seulement une partie des données est nécessaire pour la requête.

Plus important encore, chaque appel GET dans une API REST ne renvoie qu'un seul type de ressource. Par conséquent, une requête qui a besoin d'agréger des données provenant de plusieurs types de ressources devra effectuer au moins une requête pour chaque ressource comprenant les données du résultat souhaité. Pensez à une jointure du monde des bases de données relationnelles. Selon un modèle REST pur, chaque ligne renvoyée par la jointure exigerait sa propre requête GET émise sur le réseau. Ce niveau de verbosité devient rapidement un goulot d'étranglement dans une application Internet.

Quel est la solution de Yoga ?

Yoga est une boîte à outils open-source qui s'intègre à l'implémentation existante de votre serveur REST et vous permet de personnaliser votre API publique. Il ajoute un sélecteur à vos requêtes GET qui indique exactement quelles données vous vous attendez à obtenir en retour de votre requête, comme une clause de projection sur une colonne dans le monde relationnel. Il permet également d'indiquer des expressions relationnelles pouvant agréger plusieurs documents de différents types de ressources connexes en une seule réponse.

Ce n'est pas un concept nouveau. Nous l'avons vu auparavant dans des APIs publiques propriétaires, comme des versions antérieures de l'API LinkedIn et la spécification GData de Google. Mais contrairement à ces APIs, Yoga vous permet d'introduire cette syntaxe dans votre propre application REST Java.

Le reste de cette section présentera des cas d'utilisation pour lesquels Yoga permettra d'améliorer les performances des applications, tout en conservant la facilité d'utilisation d'une syntaxe REST.

Spécifier les champs d'une ressource

Voici une requête REST typique permettant de récupérer une instance d'une ressource. Elle retournera tous les champs associés au type de ressource User :

GET /user/1.json

Et si, pour des raisons de sécurité ou de performance, vous souhaitez générer une requête qui retourne uniquement le nom et l'adresse d'un utilisateur ? À des fins de développement ou pour mise à disposition à des clients de confiance, vous pouvez ajouter un sélecteur à votre requête :

GET /user/1.json?selector=(id,name,city,state,country)

Pour une API publique, vous n'avez probablement pas envie de donner aux utilisateurs finaux des capacités de sélection illimitées. Ici, vous souhaitez simplement fournir un alias vers le sélecteur défini :

GET /user/1.json?selector=$locationView

Récupérer de multiples types de ressources

Maintenant, considérons une situation où vous voulez naviguer à travers le graphe d'objets de votre modèle de domaine, et retourner des données de plusieurs classes en un seul appel à l'API :

Le graphique ci-dessus reflète le modèle de données d'entités utilisé par un client mobile ou JavaScript, qui agrège un certain nombre d'entités sur un seul écran d'information. Une telle requête à l'API émise par le client doit renvoyer des données concernant l'utilisateur, ses amis, les artistes musicaux que ses amis aiment, et les albums et chansons produites par ces artistes (ce chemin dans le graphe est surligné en bordeaux).

Des concepts comme l'utilisateur, l'artiste et la chanson sont des ressources REST distinctes, donc utiliser l'approche REST standard exige de nombreux appels réseau distincts :

GET /user/1.json (Retourne un utilisateur)  

GET /user/2.json (Retourne ses amis)     
GET /user/3.json     
...     
GET /artist/1.json (Retourne leurs artistes préférés)     
GET /artist/2.json     
...     
GET /album/1.json (Retourne les albums de ces artistes)     
GET /album/2.json     
...     
GET /song/1.json (Retourne les chansons des albums)     
GET /song/2.json     
...

De toute évidence, cela n'est pas évolutif puisque nous traversons en profondeur un graphe d'objets. La latence réseau va rapidement devenir un goulot d'étranglement pour les performances.

Avec les sélecteurs, nous pouvons indiquer toutes les données dont nous avons besoin, et les récupérer en une seule requête. Pour le développement et des clients de confiance, nous pouvons indiquer le sélecteur explicitement :

GET /user/1.json?selector=friends(favoriteArtists(albums(songs)))

Pour le déploiement en production d'une API publique, nous pouvons utiliser un Alias :

GET /user/1.json?selector=$friendsFavoriteMusic

Utiliser Yoga

L'ajout de Yoga à votre application existante nécessite des changements minimes de configuration. Yoga s'intègre proprement avec Spring MVC REST, Jersey et RESTEasy. Dans l'exemple ci-dessous, nous mettrons en œuvre Yoga dans une application Spring MVC REST.

Notre application REST utilise le MappingJacksonJsonView de Spring pour sérialiser les réponses :

    <property name="defaultViews">
      <list>
        <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"/>
      </list>
    </property>

Notre contrôleur User utilise des URIs paramétrables pour traiter les requêtes GET pour la ressource User et l'annotation Spring @ResponseBody formate la réponse avec MappingJacksonJsonView :

    @RequestMapping("/user/{id}")
    public @ResponseBody User get( @PathVariable long id ) {
      return _userRepository.fetchUser( id );
    }

Si nous avons besoin de plus de contrôle sur la façon dont les documents User sont formatés, nous pouvons migrer d'une application REST à une application Yoga. Tout d'abord, nous importons nos dépendances Maven :

    <dependency> 
      <groupId>org.skyscreamer</groupId>
      <artifactId>yoga-core</artifactId>
      <version>1.0.0</version>
    </dependency>
    <dependency>
      <groupId>org.skyscreamer</groupId>
      <artifactId>yoga-springmvc</artifactId>
      <version>1.0.0</version>
    </dependency>

Ensuite, nous remplaçons le MappingJacksonJsonView de Spring par YogaSpringView, qui sait comment analyser les sélecteurs :

    <property name="defaultViews">
      <list>
        <bean class="org.skyscreamer.yoga.springmvc.view.YogaSpringView"
              p:yogaView-ref="jsonView"/>
        <!--<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />-->
      </list>
    </property>

La dépendance injectée dans jsonView indique à SpringMVC que Yoga traitera des requêtes JSON et répondra du JSON. Nous définissons jsonView dans notre contexte d'application :

    <bean name="jsonView"
          class="org.skyscreamer.yoga.view.JsonSelectorView"
          p:selectorParser-ref="selectorParser" />
    <bean id="selectorParser" 
          class="org.skyscreamer.yoga.selector.parser.GDataSelectorParser"/>

Ici, nous précisons également que la syntaxe de la spécification GData sera utilisée pour les sélecteurs. Yoga est fourni avec une autre implémentation de SelectorParser, LinkedInSelectorParser, qui peut être utilisé par les développeurs préférant le format du sélecteur de l'API LinkedIn.

Enfin, nous enlevons les annotations @ResponseBody de notre UserController. Les annotations @ResponseBody sont dépendantes de MappingJacksonJsonView, qui a été maintenant remplacé par YogaSpringView.

    @RequestMapping("/user/{id}")
    public User get( @PathVariable long id ) {
      return_userRepository.fetchUser( id );
    }

À ce stade, le développeur peut lancer l'application Web, et ajouter les sélecteurs appropriés pour les requêtes de ressources.

GET /user/1.json?selector=id,name

retourne :

{
  "name": "Carter Page",
  "id": 1  
}

Le développeur peut ajouter favoriteArtists au sélecteur :

GET /user/1.json?selector=id,name,favoriteArtists(id,name)

et naviguer à travers le graphe d'objets pour afficher les instances de la ressource Artist :

{
  "id": 1,
  "name": "Carter Page",
  "favoriteArtists": [
    {
      "id": 1,
      "name": "Arcade Fire"           
    },
    { 
      "id": 3,
      "name": "Neutral Milk Hotel"           
    }
  ]
}

Les champs @Core

Dans notre exemple précédent, supposons que les champs id et name de User sont obligatoires et doivent être retournés pour chaque requête de la ressource User. Ceux-ci sont peu coûteux, petits, et les nommer explicitement chaque fois que nous créons un sélecteur peut devenir verbeux.

Yoga fournit une annotation @Core pouvant être appliquée à votre domaine de modèle sérialisé (ou DTO) pour identifier des champs qui seront toujours retournés sur une requête Yoga. Ici, nous annotons les getters de notre objet de domaine User :

    @Core
    public long getId() {
      return _id;
    }

    @Core
    public String getName() {
      return _name;
    }

Maintenant, nous n'avons plus besoin de demander explicitement l'id et le name dans notre sélecteur. La requête suivante :

GET /user/1.json?selector=favoriteArtists(id,name)

retournera id, name et tout ce qui est indiqué dans le sélecteur :

{
  "id": 1,
  "name": "Carter Page",
  "favoriteArtists": [
    {
      "id": 1,
      "name": "Arcade Fire"           
    },
    { 
      "id": 3,
      "name": "Neutral Milk Hotel"           
    }
  ]
}

Alias

Affiner itérativement vos sélecteurs contribue à un cycle de développement/débogage rapide. Cependant, lorsque vous arrivez au déploiement en production de votre API, vous ne voudrez peut-être pas que les utilisateurs externes composent arbitrairement des sélecteurs s'exécutant dans votre environnement. La navigation libre dans votre graphe d'objets peut rapidement conduire à des problèmes de sécurité et de performance.

Yoga vous permet de définir des alias pour les sélecteurs que vous souhaitez mettre à disposition et d'uniquement autoriser les utilisateurs à appeler les sélecteurs qui ont des alias définis. Disons que nous sommes satisfaits du sélecteur suivant et voulons le rendre public :

?selector=id,name,favoriteArtists(id,name)

Tout d'abord, dans la configuration de production, nous allons désactiver l'utilisation de sélecteurs explicites, de sorte que les utilisateurs de cet environnement ne seront pas en mesure de composer des sélecteurs en utilisant la syntaxe GData (ou LinkedIn).

    <bean id="selectorParser" 
          class="org.skyscreamer.yoga.selector.parser.GDataSelectorParser"
          p:disableExplicitSelectors="true"/>

Ensuite, nous définissons les alias. Yoga propose plusieurs mécanismes pour spécifier des alias ; dans le cas présent, nous allons les définir dans un fichier .properties.

    <bean id="aliasSelectorResolver" 
          class="org.skyscreamer.yoga.selector.parser.DynamicPropertyResolver"
          p:propertyFile="classpath:selectorAlias.properties"/>

Dans le fichier .properties, nous avons créé l'alias et l'avons nommé. Par convention, les alias Yoga commencent par un $ :

$userFavoriteArtists=id,name,favoriteArtists(id,name)

Maintenant, dans l'environnement de production, les utilisateurs sont en mesure d'invoquer les alias, dont le comportement est défini dans la documentation de notre API :

GET /user/1.json?selector=$userFavoriteArtists

Conclusion

Pour beaucoup de développeurs, le modèle REST est suffisant pour fournir l'accès à une API Web pour le domaine de votre application. Si vous avez besoin d'un contrôle plus fin de la structure de vos documents de réponses, Yoga s'intégrera à votre application REST existante, et vous permettra d'ajouter des sélecteurs à vos requêtes web.

Cette semaine (ndlt: semaine du 24/06/2013), Skyscreamer Software a publié la version 1.0 de Yoga. Yoga s'intègre directement dans les applications Java utilisant Spring MVC REST, Jersey, ou des implémentations RESTEasy. Vous pouvez trouver une vidéo de démonstration du matériel présenté dans cet article en suivant ce lien.

A propos de l'auteur

Corby Page écrit des logiciels pour l'argent et le plaisir depuis 20 ans. Il se concentre sur les solutions Java via sa société ASP Methods et il collabore avec Carter Page et Salomon Duskis dans le cadre de l'initiative open source Skyscreamer Software.

Evaluer cet article

Pertinence
Style

Contenu Éducatif

Bonjour étranger!

Vous devez créer un compte InfoQ ou cliquez sur pour déposer des commentaires. Mais il y a bien d'autres avantages à s'enregistrer.

Tirez le meilleur d'InfoQ

Html autorisé: a,b,br,blockquote,i,li,pre,u,ul,p

Commentaires de la Communauté

Html autorisé: a,b,br,blockquote,i,li,pre,u,ul,p

Html autorisé: a,b,br,blockquote,i,li,pre,u,ul,p

BT