Spring 3 中的 REST:RestTemplate

工程 | Arjen Poutsma | 2009 年 3 月 27 日 | ...

在之前的一篇文章中,我写了关于我们在 Spring @MVC 3.0 版本中添加的 REST 功能。后来,Alef 写了一篇文章,介绍了如何使用这些功能为 Pet Clinic 应用程序添加 Atom 视图。在这篇文章中,我想介绍一下我们在 Milestone 2 中添加的客户端功能。

RestTemplate

RestTemplate 是 Spring 中用于客户端 HTTP 访问的核心类。从概念上讲,它与 JdbcTemplateJmsTemplate 以及 Spring Framework 和其他组合项目中的各种其他模板非常相似。这意味着,例如,RestTemplate 在构建后是线程安全的,并且您可以使用回调来定制其操作。

RestTemplate 方法

该模板的主要入口点以六个主要的 HTTP 方法命名

HTTPRestTemplate
DELETEdelete(String, String...)
GETgetForObject(String, Class, String...)
HEADheadForHeaders(String, String...)
OPTIONSoptionsForAllow(String, String...)
POSTpostForLocation(String, Object, String...)
PUTput(String, Object, String...)

这些方法的名称清楚地表明它们调用了哪个 HTTP 方法,而名称的第二部分则表明了返回值。例如,getForObject()将执行 GET 请求,将 HTTP 响应转换为您选择的对象类型,并返回该对象。postForLocation将执行 POST 请求,将给定对象转换为 HTTP 请求,并返回响应中的 HTTP Location 头,其中可以找到新创建的对象。如您所见,这些方法试图强制遵循 REST 最佳实践。

URI 模板

这些方法都将 URI 作为第一个参数。该 URI 可以是一个 URI 模板,并且可以使用变量将模板扩展为普通 URI。模板变量可以通过两种形式传递:作为 String 变量参数数组,或作为 Map<String, String>。String varargs 变体按顺序扩展给定的模板变量,因此


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

将对以下 URI 执行 GET 请求:http://example.com/hotels/42/bookings/21。Map 变体根据变量名扩展模板,因此在使用许多变量或单个变量被多次使用时更有用。例如


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

也将对以下 URI 执行 GET 请求:http://example.com/hotels/42/rooms/42.

HttpMessageConverters

传递给方法和从方法返回的对象getForObject(), postForLocation(),以及put()HttpMessageConverters 转换为 HTTP 请求并从 HTTP 响应中转换。主要 mime 类型和 Java 类型的转换器默认注册,但您也可以编写自己的转换器并将其插入 RestTemplate。在下面的示例中,我将向您展示如何做到这一点。

使用 RestTemplate 从 Flickr 获取照片

与其逐一介绍 RestTemplate 的各种方法,不如向您展示如何使用它从雅虎的在线照片共享应用程序 Flickr 中检索图片。这个示例应用程序会在 Flickr 中搜索与给定搜索词匹配的照片,然后使用一个简单的 Swing UI 显示这些图片。要自己运行该应用程序,您需要创建一个 Flickr 帐户并申请一个 API 密钥

搜索照片

Flickr 提供了各种 API 来操作其庞大的照片库。flickr.photos.search 方法允许您通过向以下地址发出 GET 请求来搜索照片:http://www.flickr.com/services/rest?method=flickr.photos.search&api+key=xxx&tags=penguins,其中输入您的 API 密钥和要搜索的内容(本例中为企鹅)。结果是返回一个 XML 文档,描述符合您查询条件的照片。例如:

<photos page="2" pages="89" perpage="10" total="881">
	<photo id="2636" owner="47058503995@N01" 
		secret="a123456" server="2" title="test_04"
		ispublic="1" isfriend="0" isfamily="0" />
	<photo id="2635" owner="47058503995@N01"
		secret="b123456" server="2" title="test_03"
		ispublic="0" isfriend="1" isfamily="1" />
	<photo id="2633" owner="47058503995@N01"
		secret="c123456" server="2" title="test_01"
		ispublic="1" isfriend="0" isfamily="0" />
	<photo id="2610" owner="12037949754@N01"
		secret="d123456" server="2" title="00_tall"
		ispublic="1" isfriend="0" isfamily="0" />
</photos>

使用 RestTemplate,获取这样的文档非常简单:


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

其中apiKeysearchTerm是命令行上给定的两个字符串。此方法使用SourceHttpMessageConverter将 HTTP XML 响应转换为javax.xml.transform.Source(请注意,SourceHttpMessageConverter 是我们在发布 Spring 3.0 M2 后不久引入的,因此您需要获取最新的快照版本(或即将发布的 M3)才能使用它。下面提供的示例项目已配置为通过 Maven 获取这些依赖项)。

获取照片

接下来,我们将使用 XPath 表达式来获取所有photo元素。为此,我们将使用 Spring Web Services 中的 XPathTemplate。我们将执行//photo表达式,返回文档中出现的每个photo元素。NodeMapper 是一个回调接口,其mapNode()方法将为文档中的每个photo元素调用。在本例中,我们获取了该元素的server, id,以及secret属性,并用它们填充一个 Map。最后,我们再次使用 RestTemplate,将照片作为java.awt.image.BufferedImage获取。因此,当 XPath 评估完成后,结果imageList将包含 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);
    }
});

例如,给定上面提供的 XML 文档,imageList将包含 4 张图片。获取的第一张图片的 URL 将是http://static.flickr.com/2/2636_ a123456_m.jpg,第二张是http://static.flickr.com/2/2635_ b123456_m.jpg,等等。

转换图片

为了让代码工作,还需要做一件事:我们需要编写一个HttpMessageConverter,它能够从 HTTP 响应中读取并创建一个BufferedImage。使用 Java Image I/O API 实现这一点非常简单,我们只需要实现read()方法,该方法定义在HttpMessageConverter接口中。总的来说,我们简单的转换器看起来像这样:

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

}

请注意,我们没有实现write()因为我们不是上传图片,只是下载它们。现在我们只需将这个转换器插入 RestTemplate。我们在 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 id="flickrClient" class="com.springsource.samples.resttemplate.FlickrClient">
        <constructor-arg ref="restTemplate"/>
        <constructor-arg ref="xpathTemplate"/>
    </bean>

    <bean id="restTemplate" 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 id="xpathTemplate" class="org.springframework.xml.xpath.Jaxp13XPathTemplate"/>

</beans>

显示照片

最后一步是在一个简单的 GUI 中显示照片。为此,我们使用 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);

结果如下:

Penguins

总的来说,我希望这篇文章向您展示了使用 RestTemplate 与 HTTP 服务器交互是多么简单。只需不到 30 行 Java 代码,我们就创建了一个 GUI,可以显示每个人最喜欢的鸟类的照片:企鹅!查看 RestTemplate 并告诉我们您的想法!

下载

包含上述代码的 Maven 项目可以在此处下载。请注意,该项目基于 Spring 的每晚快照构建。即将发布的 Spring Milestone 3 也将包含必要的类。

获取 Spring 新闻通讯

订阅 Spring 新闻通讯,保持联系

订阅

抢占先机

VMware 提供培训和认证,助您快速进步。

了解更多

获取支持

Tanzu Spring 通过一个简单的订阅,为 OpenJDK™、Spring 和 Apache Tomcat® 提供支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看全部