Je vous propose de vous parler de REST, une architecture intéressante pour les services Webs. Après avoir expliqué les principes de REST, je vous propose de jeter un oeil à la spécification JSR-311 JAX-RS Java API for RESTful Services (mince encore du java…).
Introduction à REST
Representational State Transfert (REST) est un type d’architecture pour les systèmes distribués qui propose de représenter le modèle de données et les services sous la forme de ressources. L’URI (qui fait partie d’une URL) doit représenter une ressource unique sur le système. L’idée est de se reposer sur des méthodes standards du protocole HTTP pour manipuler une ressource. C’est une architecture presque sans état du côté du serveur. Les objets sont manipulés plutôt que d’utiliser des verbes pour modifier des ressources.
Qu’est-ce qu’une ressource ?
Une ressource est une entité facilement identifiable dans un système informatique. Ce peut être un objet du domaine comme un Restaurant mais aussi un service comme un RestaurantFormSearch par exemple.
Prenons un exemple simple: un guide des meilleurs restaurant d’Ile-de-France.
Vous devez concevoir un site internet listant les restaurants par département, par type de cuisine, par évaluation des visiteurs…
Imaginons que chaque restaurant est classé par type et par departement. Si je veux désigner Nagoya, un excellent Japonais à Créteil-Soleil (et c’est vrai), l’idée de REST est de déclarer une URI pour représenter cette ressource :
/idf/val-de-marne/creteil/japonais/nagoya
Donc par extension, si vous souhaitez voir tous les restaurants du val-de-marne, le système doit être capable de répondre si j’utilise cette ressource :
/idf/val-de-marne
Quand je dis « répondre », je veux dire, ouvrir l’url http://localhost/idf/val-de-marne dans un navigateur et recevoir en retour une liste des restaurants, structurée au format XML, HTML ou autre.
Comment lire une ressource ?
REST se repose sur HTTP et les notions de bases de l’architecture du web. La lecture d’une ressource s’effectue via la méthode GET du protocole HTTP/1.1. Oui, REST est lié à HTTP car c’est une architecture basée sur le web.
Comment créer une nouvelle ressource ?
Vous devez créer localement une nouvelle instance puis l’envoyer vers le serveur en utilisant la méthode PUT. Par analogie, lorsque vous écrivez un courrier électronique, c’est exactement le même principe. Votre email existe d’abord localement. Ensuite vous le postez au serveur.
Imaginons que vous vouliez me faire part d’un autre bon restaurant à Noisy-le-Grand. Voici comment votre système enverra au serveur une nouvelle ressource.
POST /idf/seine-saint-denis/nosiy-le-grand/japonais/nigiri
Comment effacer ? Comment mettre à jour une ressource ?
L’effacement d’une ressource se fait via la méthode DELETE du protocole HTTP. La méthode PUT permet en principe de mettre à jour une ressource existante. Cependant certains navigateurs ne gérent pa correctement ces méthodes. En particulier il n’est pas toujours possible de spécifier la méthode DELETE dan la balise FORM. Pour cette raison on contourne le problème en ajoutant un champ caché dans le formulaire et en utilisant la méthode POST.
Comparaison avec une architecture RPC classique
Prenons une application pour créer des restaurants et des types de gastronomie. Imaginez les méthode d’un web service capable d’effectuer ces traitements:
getRestaurant() addRestaurant() removeRestaurant() updateRestaurant() getCuisine() addCuisine() removeCuisine() updateCuisine() listRestaurants() listCuisines() findRestaurant() findCuisine()
Cela fait beaucoup de méthodes pour le même type de service : création, mettre à jour, effacer et lire. De plus, à chaque nouveau service vous devez déployer sur le client un nouvel adapteur ou mettre à jour la partie cliente…
Que se passe-t-il si nous basculons en architecture REST ?
Nos ressources seront accessible avec des URI simples et lisibles comme celles-ci
http://www.touilleur-express.fr:8080/restaurants/ http://www.touilleur-express.fr:8080/restaurants/LaFourchetteDor http://www.touilleur-express.fr:8080/findRestaurantForm http://www.touilleur-express.fr:8080/cuisines/ http://www.touilleur-express.fr:8080/cuisines/francais
Si vous voulez lire les données, celles-ci sont donc désignées par une URI unique. Si vous souhaitez mettre à jour la fiche d’un restaurant par exemple, cela pour s’effectuer soit via une URI de type service, soit directement en spécifiant l’URI du restaurant.
Pas mal non ?
Services Webs RESTfull
C’est une notion vraiment intéressante. Des Web Services basés sur l’architecture REST sont de plus en plus envisageables par rapport à l’utilisation de SOAP pour déployer des services. On distingue 2 grands types de Web services: ceux de type appels distants de procédures (RPC, Remote Procedure Call) et ceux de type échange de document (Document-based).
Un Web Service de type RPC est un ensemble de méthodes destinées à effectuer des appels distants. Par exemple nous pourrions voir ce genre de méthode (je caricature un peu, et évidemment qu’il ne faut pas faire ce style de code)
public void String getJapaneseRestaurantName(String country,String area, String kindOfFood)
Si nous prenons une architecture de type Document-Based, comme il est possible de le faire avec SOAP, a lieu de définir des méthodes RPC vous définissez des méthodes d’échange de ressource. Dans notre cas a lieu d’envoyer une String contenant le nom du restaurant, nous allons envoyer un objet Restaurant. Cette architecture est basée sur l’échange de document, ce qui est très proche de REST.
Un service web de type REST doit suivre les principes suivants:
- Représentation des ressources:
au lieu de ne fournir qu’un seul point d’accès (un port) et ensuite un grand nombre de méthode dans le web service, l’idée est de donner accès la ressource elle-même.
Une ressource est une partie de l’application Web que l’on désire rendre accessible. Cela peut être un avion, un vol, un aéroport, le nom d’une compagnie aérienne, un billet électronique… La ressource sera accessible du client qui en fait ce qu’il veut. Quant à l’état d’une ressource, comme par exempl « billet électronique réservé, billet annulé » c’est un état qui sera stocké dans la ressource elle-même. Faire la distinction entre le modèle de base et son état est l’une des difficultés de REST. - Adressable et accessibe sur le réseau.
Une ressource est caractérisée par son type (aéroport) et par son URI (france/paris/orly) afin de l’identifier et de la localiser. De même un web service fournira des services pour des ressources de manière moins visible qu’une architecture de type RPC. - Accessible sur le réseau
Une URI n’est pas suffisante pour qu’un client accède à une ressource. Il faut que l’interface d’accès de la ressource soit identifiable et unique. Si c’est la méthode GET du protocole HTTP qui est utilisée pour lire une ressource, il faut que cette manière soit uniforme dans l’architecture. - Sans état. Ah c’est un vaste sujet difficile. La puissance d’une architecture REST est de dire que le serveur n’est pas responsable de l’état d’une ressource. Dans une architecture REST, il n’y a aucuns états sur le serveur. C’est au client de stocker l’état de la ressource, et de la lire ou de la retourner au serveur. Ce système permet de bénéficier d’une architecture en cluster très facilement. Votre application va créer une ressource de type « Restaurant ». Votre client gère son état (« en attente de
revue », « testé », « resto fermé ») et il se contente d’envoyer ou de lire la ressource avec le serveur. Dans une architecture de type RPC, vous auriez des méthodes (des verbes) pour modifier la ressource qui est sur le serveur. Dans une architecture REST, vous utilisez des ressources (des noms) pour modifier les objets du côté du client. Le client est responsable de l’état de la ressource. Le serveur donne accès à une version de la ressource.
Cache, cluster et grids sont sur un radeau…
A partir du moment où l’on parle de ressource, tout ce qui est mise en cache du côté du client prend un vrai sens. En effet vous pouvez utiliser les spécifications du protocole HTTP/1.1 pour réduire les échanges entre un client et un serveur. Il suffit lorsqu’une ressource est retournée d’ajouter une date d’expiration ou un ETag HTTP/1.1. Cela permet alors d’utiliser les principes du web pour une application enfin intelligente.
La spécification JSR-311
Cette spécification vise à proposer une nouvelle librairie légère pour Java afin de permettre d’utiliser une architecture REST. Pour cela, les idées des personnes qui rédigent cette spécification est d’utiliser des annotations pour marquer une class simple et la rendre donc accessible via REST.
Au niveau de la couche de protocole, REST est lié à HTTP, même si des extensions comme Webdav seront supportées. Au niveau du contenu échangé entre le navigateur et le serveur, la bonne idée est de n pas nous forcer à utiliser soit du XML, soit du JSON, soit autre chose. Le contenu négocié sera donc indépendant de ce que l’API propose. C’est une bonne chose. Enfin l’objectif de l’équipe est que cette JSR fasse partie de Java EE6. J’ai lu la spec, mais j’avoue que je n’ai pas tout repris ici. Si vous voulez des détails, le mieux est d’aller la lire en ligne sur le site de la JSR.
Détails sur l’implémentation (une histoire de resto…)
JAX-RS fonctionnera en mode client seul ou en tant que Servlet embarquée dans un conteneur léger. Concernant les annotations, une ressource sera une classe avec au moins une annotation @Path présente sur l’une de ses méthodes ou sur la classe.
Par exemple une ressource qui représente des Restaurants :
@Path("restaurants") public class Restaurants { ... }
Après compilation et déploiement dans un conteneur de Servlets comme Tomcat, votre liste de restaurant sera donc accessible avec une url de ce type:
http://localhost:8080/rest/restaurants
Cependant ce n’est pas suffisant. Ensuite les annotations suivantes permettront de déclarer les méthodes de type REST:
@GET, @POST, @PUT, @DELETE et @HEAD.
Si je souhaite une uri /restaurants/chinois par exemple il faut que j’ajoute une méthode et qu’elle soit marquée avec un Path comme sur cet exemple :
@Path("restaurants") public class Restaurants { @GET @Path("chinois") public List getListOfChineeseRestaurants()
Concernant l’injection de paramètres à une méthode, si par exemple vous avez déclaré une méthode
findRestaurant et que vous souhaitez injecter des paramètres, JAX-RS vous permettra d’annoter la
méthode afin de recevoir par injection, les paramètres décryptés de l’URI…
ok je pense que là personne n’a compris…
Soit l’uri suivante: /restaurants/laTourDargent
Imaginons que votre Pojo dispose d’une méthode générique pour lister les restaurants.
Comment injecter alors le critere « laTourDargent »?
@Path("restaurants") public class Restaurants { @Path("{name}") public Restaurant getRestaurantByName( @UriParam("name") String restaurantName) {
.... }
Gestion des formats
Selon que vous souhaitez envoyer du xml, du html ou autre, vous pouvez aussi annoter chaque méthode ou chaque class avec une annotation de contenu. Si je souhaite afficher une photo du restaurant, je peux déclarer une méthode getPhotoForRestaurant.
@Path("restaurants") @ProduceMime("text/html") public class Restaurants { @Path("{name}") public Restaurant getRestaurantByName( @UriParam("name") String restaurantName) { .... } @Path("{name}/photo") @ProduceMime("text/jpg") public String getPhotoForRestaurant( @UriParam("name") String restaurantName) { .... }
Attention cependant, ici je retournerai un buffer binaire empaqueté dans une String, ce n’est pas ce qu’il faut faire.
Pour gérer les différents types de média, la JSR-311 introduit la notion de Provider. Un provider peut recevoir ou envoyer des ressources selon différentes formats (xml, text, binaires…). Le Provider se charge aussi de l’encoding lorsque c’est nécessaire.
Les contextes
Il est possible d’injecter les paramètres HTTP ou de lire des valeurs des entêtes HTTP en utilisant une annotation @Context. Cela permettra par exemple de retourner du XML si le client est un RSS Reader, ou de retourner du HTML pour un client web. Il sera aussi possible de récuperer les paramètres HTTP afin d’effectuer un traitement.
@Path("restaurants") @ProduceMime("text/html") public class Restaurants { public List findRestaurants(@Context UriInfo info) { // info contains bookmarked uri with search // criteria for my touilleur restaurant... // Call the restaurant service } }
Les contextes qui peuvent être injectés :
– les headers HTTP
– la requête courante
– ServletConfig, ServletContext, HttpServletRequest, HttpServletResponse
En principe nous pourrons aussi injecter des références vers des EJB3 dans un serveur d’application. J’imagine qu’il est prévu de faire de même pour les datasources… Cela dit là on marche sur les plates-bandes de la JSR-299, Web Beans, largement inspiré du framework JBoss Seam.
Je m’arrête là, j’espère avoir donné l’envie à quelques uns d’aller tester ou de s’intéresser à la JSR-311 pour leurs applications.
Je veux tester !
Il vous faut un serveur Tomcat, un peu d’huile de coude et enfin l’implémentation de référence de la
JSR-311, à savoir Jersey.
JBoss Seam travaille actuellement pour offrie un support compatible à la JSR-311. Pour cela donc, on pourra utiliser les annotations dont j’ai parlé directement dans Seam.
http://www.seamframework.org/Documentation/RESTfulApplicationsWithSeam
A noter qu’en 2007, JBoss Seam et la JSR-311 faisaient partis tous les deux des 10 sujets recommandés dans l’article Top 10 Destinations for Enterprise Developers at the 2007 JavaOne Conference
Ressources:
Le site de la JSR-311 https://jsr311.dev.java.net/
Jersey, reference implementation of JSR-311 https://jersey.dev.java.net/
The JBoss Seam framework and REST http://www.seamframework.org/Documentation/RESTfulApplicationsWithSeam
JBoss propose une implémentation de la JSR-311 appelée RESTeasyJAXRS
http://wiki.jboss.org/wiki/RESTeasyJAXRS
Attention c’est en béta pour l’instant