Spring Blog
In un post precedente, ho parlato delle funzionalità REST che abbiamo aggiunto a Spring @MVC versione 3.0. Più tardi, Alef ha scritto sull’uso della funzionalità introdotta per aggiungere una vista Atom all’applicazione Pet Clinic. In questo post, vorrei introdurre le funzionalità lato client che abbiamo aggiunto in Milestone 2.
RestTemplate
Il RestTemplate è la classe centrale di Spring per l’accesso HTTP lato client. Concettualmente, è molto simile a JdbcTemplate, JmsTemplate, e ai vari altri template che si trovano nello Spring Framework e in altri progetti di portfolio. Questo significa, per esempio, che il RestTemplate è thread-safe una volta costruito, e che si possono usare callback per personalizzare le sue operazioni.
Metodi del RestTemplate
I principali punti di ingresso del template prendono il nome dai sei principali metodi HTTP:
HTTP | RestTemplate |
---|---|
DELETE | delete(String, String…) |
GET | getForObject(String, Class, String…) |
HEAD | headForHeaders(String, String…) |
OPTIONS | optionsForAllow(String, String…) |
POST | postForLocation(String, Object, String…) |
PUT | put(String, Object, String…) |
I nomi di questi metodi indicano chiaramente quale metodo HTTP invocano, mentre la seconda parte del nome indica cosa viene restituito. Per esempio, getForObject() eseguirà un GET, convertirà la risposta HTTP in un tipo di oggetto di vostra scelta, e restituirà quell’oggetto. postForLocation eseguirà un POST, convertendo l’oggetto dato in una richiesta HTTP, e restituirà l’header di risposta HTTP Location dove l’oggetto appena creato può essere trovato. Come potete vedere, questi metodi cercano di applicare le migliori pratiche REST.
Modelli di URI
Ognuno di questi metodi prende un URI come primo argomento. Questo URI può essere un template URI, e le variabili possono essere usate per espandere il template in un URI normale. Le variabili del template possono essere passate in due forme: come array di argomenti variabili String, o come Map<String, String>. La variante string varargs espande le variabili del template date in ordine, così che
String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21");
eseguirà un GET su http://example.com/hotels/42/bookings/21. La variante map espande il template in base al nome della variabile, ed è quindi più utile quando si usano molte variabili, o quando una singola variabile è usata più volte. Per esempio:
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);
eseguirà anche un GET su http://example.com/hotels/42/rooms/42.
HttpMessageConverters
Gli oggetti passati e restituiti dai metodi getForObject(), postForLocation() e put() e sono convertiti in richieste HTTP e da risposte HTTP da HttpMessageConverters. I convertitori per i principali tipi mime e i tipi Java sono registrati di default, ma è anche possibile scrivere il proprio convertitore e inserirlo nel RestTemplate. Nell’esempio che segue, vi mostrerò come si fa.
Usare il RestTemplate per recuperare le foto da Flickr
Piuttosto che passare attraverso i vari metodi del RestTemplate, vi mostrerò come usarlo per recuperare le foto da Flickr, l’applicazione online di condivisione foto di Yahoo! Questa applicazione di esempio cerca su Flickr le foto che corrispondono ad un dato termine di ricerca. Poi mostra queste immagini usando una semplice interfaccia utente Swing. Per eseguire l’applicazione da soli, è necessario creare un account Flickr e richiedere una chiave API.
Ricerca di foto
Flickr espone varie API per manipolare la sua vasta libreria di foto. Il metodo flickr.photos.search ti permette di cercare le foto, emettendo una richiesta GET su http://www.flickr.com/services/rest?method=flickr.photos.search&api+key=xxx&tags=penguins, dove inserisci la tua chiave API e la cosa da cercare (pinguini in questo caso). Come risultato, si ottiene un documento XML che descrive le foto conformi alla vostra richiesta. Qualcosa come:
<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>
Utilizzando il RestTemplate, recuperare un tale documento è abbastanza banale:
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);
dove apiKey e searchTerm sono due stringhe date sulla linea di comando. Questo metodo usa il SourceHttpMessageConverter per convertire la risposta HTTP XML in un javax.xml.transform.Source (Notate che il SourceHttpMessageConverter è stato introdotto poco dopo il rilascio di Spring 3.0 M2, quindi dovrete ottenere uno snapshot recente (o il prossimo M3) per utilizzarlo. Il progetto di esempio disponibile qui sotto è impostato per recuperarli tramite Maven).
Ricerca delle foto
In seguito, useremo un’espressione XPath per recuperare tutti gli elementi delle foto del documento. Per questo, useremo XPathTemplate di Spring Web Services. Eseguiremo le //espressioni di foto, restituendo tutti gli elementi di foto che si trovano in qualsiasi punto del documento. Il NodeMapper è un’interfaccia di callback, il cui metodo mapNode() sarà invocato per ogni elemento fotografico nel documento. In questo caso, stiamo recuperando gli attributi server, id e secret di questo elemento, e li usiamo per riempire una mappa. Infine, usiamo di nuovo il RestTemplate per recuperare la foto come java.awt.image.BufferedImage. Così, quando la valutazione XPath è fatta, la imageList risultante conterrà un’immagine per ogni foto nel documento 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); }});
Per esempio, dato il documento XML dato sopra, la imageList conterrà 4 immagini. L’URL della prima immagine recuperata sarà http://static.flickr.com/2/2636_ a123456_m.jpg, la seconda è http://static.flickr.com/2/2635_ b123456_m.jpg, ecc.
Convertire le immagini
C’è ancora una cosa da fare perché il codice funzioni: dobbiamo scrivere un HttpMessageConverter che sia in grado di leggere la risposta HTTP e creare una BufferedImage da questa. Farlo con l’API Java Image I/O è abbastanza semplice, dobbiamo solo implementare il metodo read() definito nell’interfaccia HttpMessageConverter. Nel complesso, il nostro semplice convertitore assomiglia a questo:
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"); }}
Nota che non abbiamo implementato write() perché non stiamo caricando immagini, solo scaricandole. Ora dobbiamo solo inserire questo convertitore nel RestTemplate. Lo facciamo nel contesto dell’applicazione 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>
Mostra le foto
La fase finale è mostrare le foto in una semplice interfaccia grafica. Per questo, usiamo 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);
che ci dà quanto segue:
In definitiva, spero che questo post vi abbia mostrato quanto possa essere semplice usare il RestTemplate per interagire con i server HTTP. In poco meno di 30 righe di codice Java, abbiamo creato una GUI che mostra le immagini dell’uccello preferito da tutti: il pinguino! Date un’occhiata al RestTemplate e fateci sapere cosa ne pensate!
Downloads
Un progetto Maven contenente il codice di cui sopra può essere scaricato qui. Si noti che il progetto è basato su una build snapshot notturna di Spring. La prossima Milestone 3 di Spring conterrà anche le classi necessarie.