Articles

Spring Blog

In einem früheren Beitrag habe ich über die REST-Funktionen gebloggt, die wir der Spring @MVC Version 3.0 hinzugefügt haben. Später schrieb Alef über die Verwendung der eingeführten Funktionalität, um der Pet Clinic-Anwendung eine Atom-Ansicht hinzuzufügen. In diesem Beitrag möchte ich die clientseitigen Fähigkeiten vorstellen, die wir in Milestone 2 hinzugefügt haben.

RestTemplate

Das RestTemplate ist die zentrale Spring-Klasse für den clientseitigen HTTP-Zugriff. Konzeptionell ist es dem JdbcTemplate, JmsTemplate und den verschiedenen anderen Templates, die im Spring Framework und anderen Portfolio-Projekten zu finden sind, sehr ähnlich. Das bedeutet zum Beispiel, dass das RestTemplate thread-sicher ist, sobald es aufgebaut ist, und dass Sie Callbacks verwenden können, um seine Operationen anzupassen.

RestTemplate-Methoden

Die Haupteinstiegspunkte des Templates sind nach den sechs wichtigsten HTTP-Methoden benannt:

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…)

Die Namen dieser Methoden zeigen deutlich, welche HTTP-Methode sie aufrufen, während der zweite Teil des Namens angibt, was zurückgegeben wird. Zum Beispiel führt getForObject() einen GET aus, konvertiert die HTTP-Antwort in einen Objekttyp Ihrer Wahl und gibt dieses Objekt zurück. postForLocation führt einen POST aus, konvertiert das angegebene Objekt in eine HTTP-Anfrage und gibt den HTTP-Location-Header der Antwort zurück, in dem das neu erstellte Objekt gefunden werden kann. Wie Sie sehen können, versuchen diese Methoden, REST-Best-Practices durchzusetzen.

URI-Vorlagen

Jede dieser Methoden nimmt einen URI als erstes Argument. Dieser URI kann ein URI-Template sein, und Variablen können verwendet werden, um das Template zu einem normalen URI zu erweitern. Die Template-Variablen können in zwei Formen übergeben werden: als String-Variablen-Array oder als Map<String, String>. Die String-Vargs-Variante expandiert die angegebenen Template-Variablen der Reihe nach, so dass


String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21");

ein GET auf http://example.com/hotels/42/bookings/21 durchgeführt wird. Die map-Variante erweitert das Template auf Basis des Variablennamens und ist daher sinnvoller, wenn viele Variablen verwendet werden oder wenn eine einzelne Variable mehrfach verwendet wird. Zum Beispiel:


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);

führt auch einen GET auf http://example.com/hotels/42/rooms/42 aus.

HttpMessageConverters

Objekte, die an die Methoden getForObject(), postForLocation() und put() übergeben und von diesen zurückgegeben werden, werden von HttpMessageConverters in HTTP-Anfragen und aus HTTP-Antworten konvertiert. Standardmäßig sind Konverter für die wichtigsten Mime-Typen und Java-Typen registriert, aber Sie können auch Ihren eigenen Konverter schreiben und ihn in das RestTemplate einfügen. Im folgenden Beispiel zeige ich Ihnen, wie das geht.

Verwenden des RestTemplate zum Abrufen von Fotos von Flickr

Anstatt die verschiedenen Methoden des RestTemplate durchzugehen, zeige ich Ihnen, wie Sie es zum Abrufen von Bildern von Flickr, der Online-Foto-Sharing-Anwendung von Yahoo! verwenden. Diese Beispielanwendung durchsucht Flickr nach Fotos, die einem bestimmten Suchbegriff entsprechen. Anschließend zeigt sie diese Bilder mit einer einfachen Swing-Oberfläche an. Um die Anwendung selbst auszuführen, müssen Sie ein Flickr-Konto erstellen und einen API-Schlüssel beantragen.

Suchen nach Fotos

Flickr stellt verschiedene APIs zur Verfügung, um seine riesige Bibliothek von Fotos zu bearbeiten. Mit der Methode flickr.photos.search können Sie nach Fotos suchen, indem Sie eine GET-Anfrage an http://www.flickr.com/services/rest?method=flickr.photos.search&api+key=xxx&tags=penguins stellen, in die Sie Ihren API-Schlüssel und die Sache, nach der Sie suchen wollen (in diesem Fall Pinguine), eingeben. Als Ergebnis erhalten Sie ein XML-Dokument zurück, das die Fotos beschreibt, die Ihrer Anfrage entsprechen. So etwas wie:


<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>

Unter Verwendung des RestTemplates ist das Abrufen eines solchen Dokuments recht trivial:


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);

wobei apiKey und searchTerm zwei Strings sind, die in der Kommandozeile angegeben werden. Diese Methode verwendet den SourceHttpMessageConverter, um die HTTP-XML-Antwort in eine javax.xml.transform.Source zu konvertieren (Beachten Sie, dass der SourceHttpMessageConverter kurz nach der Veröffentlichung von Spring 3.0 M2 eingeführt wurde, sodass Sie einen aktuellen Snapshot (oder das kommende M3) benötigen, um ihn zu verwenden. Das unten verfügbare Beispielprojekt ist so eingerichtet, dass es diese über Maven abruft).

Abrufen der Fotos

Als Nächstes werden wir einen XPath-Ausdruck verwenden, um alle Fotoelemente des Dokuments abzurufen. Hierfür verwenden wir das XPathTemplate von Spring Web Services. Wir werden die //photo-Ausdrücke ausführen und alle Fotoelemente zurückgeben, die irgendwo im Dokument vorkommen. Der NodeMapper ist eine Callback-Schnittstelle, deren mapNode()-Methode für jedes Fotoelement im Dokument aufgerufen wird. In diesem Fall holen wir die Attribute server, id und secret dieses Elements ab und verwenden diese, um eine Map aufzufüllen. Schließlich verwenden wir wieder das RestTemplate, um das Foto als java.awt.image.BufferedImage abzurufen. Wenn die XPath-Auswertung abgeschlossen ist, enthält die resultierende imageList also ein Bild für jedes Foto im XML-Dokument.


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); }});

Beim oben angegebenen XML-Dokument enthält die imageList beispielsweise 4 Bilder. Die URL für das erste abgefragte Bild ist http://static.flickr.com/2/2636_ a123456_m.jpg, das zweite ist http://static.flickr.com/2/2635_ b123456_m.jpg, usw.

Konvertieren der Bilder

Es gibt noch eine Sache, die getan werden muss, damit der Code funktioniert: Wir müssen einen HttpMessageConverter schreiben, der in der Lage ist, aus der HTTP-Antwort zu lesen und daraus ein BufferedImage zu erstellen. Dies mit der Java Image I/O API zu tun, ist ziemlich einfach, wir müssen nur die read()-Methode implementieren, die in der HttpMessageConverter-Schnittstelle definiert ist. Insgesamt sieht unser einfacher Konverter so aus:


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"); }}

Beachten Sie, dass wir write() nicht implementiert haben, weil wir keine Bilder hochladen, sondern nur herunterladen. Jetzt müssen wir nur noch diesen Konverter in das RestTemplate einbinden. Das machen wir im Spring-Anwendungskontext:


<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>

Anzeigen der Fotos

Der letzte Schritt ist das Anzeigen der Fotos in einem einfachen GUI. Dafür verwenden wir 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);

Damit erhalten wir folgendes:

Penguins

Zusammenfassend hoffe ich, dass dieser Beitrag Ihnen gezeigt hat, wie einfach es sein kann, das RestTemplate für die Interaktion mit HTTP-Servern zu verwenden. In nur knapp 30 Zeilen Java-Code haben wir eine GUI erstellt, die Bilder von jedermanns Lieblingsvogel zeigt: dem Pinguin! Probieren Sie das RestTemplate aus und lassen Sie uns wissen, was Sie davon halten!

Downloads

Ein Maven-Projekt, das den obigen Code enthält, kann hier heruntergeladen werden. Beachten Sie, dass das Projekt auf einem nächtlichen Snapshot-Build von Spring basiert. Der kommende Milestone 3 von Spring wird die notwendigen Klassen ebenfalls enthalten.

Eine Antwort schreiben

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.