Blog Spring
Dans un précédent billet, j’ai parlé des fonctionnalités REST que nous avons ajoutées à Spring @MVC version 3.0. Plus tard, Alef a écrit sur l’utilisation de la fonctionnalité introduite pour ajouter une vue Atom à l’application Pet Clinic. Dans ce billet, je voudrais présenter les capacités côté client que nous avons ajoutées dans le Milestone 2.
RestTemplate
Le RestTemplate est la classe centrale de Spring pour l’accès HTTP côté client. Conceptuellement, elle est très similaire à JdbcTemplate, JmsTemplate, et aux divers autres modèles que l’on trouve dans le Spring Framework et dans d’autres projets du portefeuille. Cela signifie, par exemple, que le RestTemplate est thread-safe une fois construit, et que vous pouvez utiliser des callbacks pour personnaliser ses opérations.
Méthodes de RestTemplate
Les principaux points d’entrée du modèle sont nommés d’après les six principales méthodes HTTP :
HTTP | RestTemplate |
---|---|
DELETE | delete(String, String…) | Tr> | GET | getForObject(String, Class, String….) | headForHeaders(String, String…) | optionsForAllow(String, String…) | POST | postForLocation(String, Object, String….) | PUT | put(String, Object, String…) |
Les noms de ces méthodes indiquent clairement quelle méthode HTTP elles invoquent, tandis que la deuxième partie du nom indique ce qui est renvoyé. Par exemple, getForObject() effectuera un GET, convertira la réponse HTTP en un type d’objet de votre choix, et renverra cet objet. postForLocation effectuera un POST, en convertissant l’objet donné en une requête HTTP, et renverra l’en-tête de localisation HTTP de la réponse où l’objet nouvellement créé peut être trouvé. Comme vous pouvez le voir, ces méthodes essaient d’appliquer les meilleures pratiques REST.
Modèles d’URI
Chacune de ces méthodes prend un URI comme premier argument. Cet URI peut être un modèle d’URI, et des variables peuvent être utilisées pour étendre le modèle à un URI normal. Les variables du modèle peuvent être passées sous deux formes : comme un tableau de varargs de type String, ou comme une Map<String, String>. La variante string varargs développe les variables de modèle données dans l’ordre, de sorte que
String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21");
réalisera un GET sur http://example.com/hotels/42/bookings/21. La variante map développe le modèle en fonction du nom de la variable, et est donc plus utile lorsqu’on utilise de nombreuses variables, ou lorsqu’une seule variable est utilisée plusieurs fois. Par exemple :
Map<String, String> vars = new HashMap<String, String>();vars.put("hotel", "42");vars.put("booking", "21");String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}", String.class, vars);
Effectuera également un GET sur http://example.com/hotels/42/rooms/42.
HttpMessageConverters
Les objets passés et retournés par les méthodes getForObject(), postForLocation() et put() et sont convertis en requêtes HTTP et à partir des réponses HTTP par des HttpMessageConverters. Les convertisseurs pour les principaux types mime et les types Java sont enregistrés par défaut, mais vous pouvez également écrire votre propre convertisseur et l’insérer dans le RestTemplate. Dans l’exemple ci-dessous, je vais vous montrer comment faire.
Utiliser le RestTemplate pour récupérer des photos de Flickr
Plutôt que de passer en revue les différentes méthodes du RestTemplate, je vais vous montrer comment l’utiliser pour récupérer des photos de Flickr, l’application de partage de photos en ligne de Yahoo!s. Cet exemple d’application recherche sur Flickr les photos qui correspondent à un terme de recherche donné. Elle affiche ensuite ces photos à l’aide d’une simple interface utilisateur Swing. Pour exécuter l’application vous-même, vous devrez créer un compte Flickr et demander une clé API.
Recherche de photos
Flickr expose diverses API pour manipuler sa vaste bibliothèque de photos. La méthode flickr.photos.search vous permet de rechercher des photos, en émettant une requête GET sur http://www.flickr.com/services/rest?method=flickr.photos.search&api+key=xxx&tags=penguins, où vous entrez votre clé API et la chose à rechercher (des pingouins dans ce cas). En conséquence, vous obtenez en retour un document XML, décrivant les photos conformes à votre requête. Quelque chose comme:
<photos page="2" pages="89" perpage="10" total="881"><photo owner="" secret="a123456" server="2" title="test_04"ispublic="1" isfriend="0" isfamily="0" /><photo owner=""secret="b123456" server="2" title="test_03"ispublic="0" isfriend="1" isfamily="1" /><photo owner=""secret="c123456" server="2" title="test_01"ispublic="1" isfriend="0" isfamily="0" /><photo owner=""secret="d123456" server="2" title="00_tall"ispublic="1" isfriend="0" isfamily="0" /></photos>
Utilisant le RestTemplate, la récupération d’un tel document est assez triviale:
final String photoSearchUrl = "http://www.flickr.com/services/rest?method=flickr.photos.search&api+key={api-key}&tags={tag}&per_page=10";Source photos = restTemplate.getForObject(photoSearchUrl, Source.class, apiKey, searchTerm);
où apiKey et searchTerm sont deux Strings donnés sur la ligne de commande. Cette méthode utilise le SourceHttpMessageConverter pour convertir la réponse HTTP XML en une source javax.xml.transform.Source (Notez que le SourceHttpMessageConverter a été introduit peu de temps après que nous ayons publié Spring 3.0 M2, donc vous devrez obtenir un snapshot récent (ou le prochain M3) pour l’utiliser. Le projet exemple disponible ci-dessous est configuré pour les récupérer via Maven).
Retrouver les photos
Puis, nous allons utiliser une expression XPath pour récupérer tous les éléments photos du document. Pour cela, nous allons utiliser le XPathTemplate de Spring Web Services. Nous allons exécuter les expressions //photo, en retournant tous les éléments photo se trouvant n’importe où dans le document. Le NodeMapper est une interface de rappel, dont la méthode mapNode() sera invoquée pour chaque élément photo du document. Dans ce cas, nous récupérons les attributs server, id, et secret de cet élément, et nous les utilisons pour remplir une Map. Enfin, nous utilisons à nouveau le RestTemplate pour récupérer la photo sous forme de java.awt.image.BufferedImage. Ainsi, lorsque l’évaluation XPath est effectuée, la liste d’images résultante contiendra une image pour chaque photo du document XML.
List<BufferedImage> imageList = xpathTemplate.evaluate("//photo", photos, new NodeMapper() { public Object mapNode(Node node, int i) throws DOMException { Element photo = (Element) node; Map<String, String> variables = new HashMap<String, String>(3); variables.put("server", photo.getAttribute("server")); variables.put("id", photo.getAttribute("id")); variables.put("secret", photo.getAttribute("secret")); String photoUrl = "http://static.flickr.com/{server}/{id}_{secret}_m.jpg"; return restTemplate.getForObject(photoUrl, BufferedImage.class, variables); }});
Par exemple, étant donné le document XML donné ci-dessus, la liste d’images contiendra 4 images. L’URL de la première image récupérée sera http://static.flickr.com/2/2636_ a123456_m.jpg, la deuxième est http://static.flickr.com/2/2635_ b123456_m.jpg, etc.
Conversion des images
Il reste encore une chose à faire pour que le code fonctionne : nous devrons écrire un HttpMessageConverter capable de lire la réponse HTTP, et de créer un BufferedImagefrom from that. Le faire avec l’API Java Image I/O est assez simple, nous devons juste implémenter la méthode read() définie dans l’interface HttpMessageConverter. Globalement, notre convertisseur simple ressemble à ceci:
public class BufferedImageHttpMessageConverter implements HttpMessageConverter<BufferedImage> { public List<MediaType> getSupportedMediaTypes() { return Collections.singletonList(new MediaType("image", "jpeg")); } public boolean supports(Class<? extends BufferedImage> clazz) { return BufferedImage.class.equals(clazz); } public BufferedImage read(Class<BufferedImage> clazz, HttpInputMessage inputMessage) throws IOException { return ImageIO.read(inputMessage.getBody()); } public void write(BufferedImage image, HttpOutputMessage message) throws IOException { throw new UnsupportedOperationException("Not implemented"); }}
Notez que nous n’avons pas implémenté write() parce que nous ne téléchargeons pas d’images, mais seulement des images. Maintenant, nous devons juste brancher ce convertisseur dans le RestTemplate. Nous faisons cela dans le contexte de l’application Spring:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean class="com.springsource.samples.resttemplate.FlickrClient"> <constructor-arg ref="restTemplate"/> <constructor-arg ref="xpathTemplate"/> </bean> <bean class="org.springframework.web.client.RestTemplate"> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/> <bean class="com.springsource.samples.resttemplate.BufferedImageHttpMessageConverter"/> </list> </property> </bean> <bean class="org.springframework.xml.xpath.Jaxp13XPathTemplate"/></beans>
Montrer les photos
La dernière étape consiste à montrer les photos dans une interface graphique simple. Pour cela, nous utilisons Swing:
JFrame frame = new JFrame(searchTerm + " photos");frame.setLayout(new GridLayout(2, imageList.size() / 2));for (BufferedImage image : imageList) { frame.add(new JLabel(new ImageIcon(image)));}frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.pack();frame.setVisible(true);
ce qui nous donne ce qui suit:
Dans l’ensemble, j’espère que ce post vous a montré à quel point il peut être simple d’utiliser le RestTemplate pour interagir avec les serveurs HTTP. En un peu moins de 30 lignes de code Java, nous avons créé une interface graphique qui affiche des photos de l’oiseau préféré de tous : le pingouin ! Jetez un coup d’œil au RestTemplate et faites-nous savoir ce que vous en pensez !
Téléchargements
Un projet Maven contenant le code ci-dessus peut être téléchargé ici. Notez que le projet est basé sur une construction snapshot nocturne de Spring. La prochaine Milestone 3 de Spring contiendra également les classes nécessaires.
Il s’agit d’un projet Maven.