Spring 3 中的 REST:RestTemplate

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

在之前的一篇文章中,我写了关于我们添加到 Spring @MVC 3.0 版本中的 REST 功能。后来,Alef撰写了关于使用引入的功能向 Pet Clinic 应用程序添加 Atom 视图的文章。在这篇文章中,我想介绍我们在里程碑 2 中添加的客户端功能。

RestTemplate

RestTemplate 是客户端 HTTP 访问的核心 Spring 类。从概念上讲,它与JdbcTemplateJmsTemplate以及 Spring 框架和其他产品组合项目中发现的各种其他模板非常相似。这意味着,例如,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。模板变量可以通过两种形式传递:作为字符串变量参数数组或作为 Map<String, String>。字符串 varargs 变体按顺序扩展给定的模板变量,因此


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

将在以下位置执行 GET 操作:http://example.com/hotels/42/bookings/21。映射变体根据变量名称扩展模板,因此在使用多个变量时或当单个变量多次使用时更有用。例如


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

也将执行以下 GET 操作:http://example.com/hotels/42/rooms/42.

HttpMessageConverters

传递给方法的对象以及从方法返回的对象getForObject(), postForLocation()put()HttpMessageConverters转换为 HTTP 请求并从 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, idsecret属性,并使用它们填充 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 里程碑 3 也将包含必要的类。

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,以加速您的进步。

了解更多

获得支持

Tanzu Spring 在一个简单的订阅中提供对 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

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

查看全部