2009-04-09 03:29:02 +08:00
|
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
2009-04-11 12:49:11 +08:00
|
|
|
<chapter id="rest">
|
|
|
|
|
<title>REST support</title>
|
2009-04-09 03:29:02 +08:00
|
|
|
|
2009-04-11 13:59:12 +08:00
|
|
|
<section id="rest-introduction">
|
2009-04-09 03:29:02 +08:00
|
|
|
<title>Introduction</title>
|
|
|
|
|
|
|
|
|
|
<para>The goal of Spring's REST support is to make the development of
|
2009-04-14 14:59:22 +08:00
|
|
|
RESTful Web services and applications easier. </para>
|
|
|
|
|
|
|
|
|
|
<para>Client-side access to RESTful resources is greatly simplified using
|
|
|
|
|
Spring <classname>RestTemplate</classname>.
|
|
|
|
|
<classname>RestTemplate</classname> follows in the footsteps of other
|
|
|
|
|
template classes in Spring such as <classname>JdbcTemplate</classname> and
|
2009-04-09 03:29:02 +08:00
|
|
|
<classname>JmsTemplate</classname>. Instead of dealing with a verbose
|
2009-04-14 14:59:22 +08:00
|
|
|
lower level API such as Apache Commons <classname>HttpClient</classname>
|
|
|
|
|
to create RESTful request, RestTemplate provides one liner methods that
|
|
|
|
|
are purpose built for RESTful programming. </para>
|
|
|
|
|
|
|
|
|
|
<para>On the server-side, Spring's REST support is based upon Spring's
|
|
|
|
|
existing annotation based MVC framework. (For those interested in the
|
|
|
|
|
rational for that decision, and for not implementing JAX-RS, read Arjen
|
|
|
|
|
Poutsma's SpringSource TeamBlog <ulink
|
2009-04-09 03:29:02 +08:00
|
|
|
url="http://blog.springsource.com/2009/03/08/rest-in-spring-3-mvc/">entry</ulink>.)
|
|
|
|
|
With little effort, you can marshall data out of a RESTful request using
|
2009-04-14 14:59:22 +08:00
|
|
|
<classname>@RequestMapping</classname> and
|
|
|
|
|
<classname>@PathVariable</classname> annotations and return different
|
|
|
|
|
views as determined by the request's <literal>Context-Type</literal>
|
|
|
|
|
header.</para>
|
2009-04-09 03:29:02 +08:00
|
|
|
|
|
|
|
|
<para>In this chapter we describe all the features of Spring's REST
|
|
|
|
|
support. It is divided into two main two chapters, one for the server-side
|
|
|
|
|
and one for the client-side. For those new to Spring's <link
|
|
|
|
|
linkend="mvc">MVC framework</link>, you may want to read through the
|
|
|
|
|
reference documentation on <link linkend="mvc-annotation">annotation-based
|
2009-04-14 14:59:22 +08:00
|
|
|
controller configuration</link> to understand the general programming
|
2009-04-11 12:49:11 +08:00
|
|
|
model.</para>
|
2009-04-09 03:29:02 +08:00
|
|
|
</section>
|
|
|
|
|
|
2009-04-11 13:59:12 +08:00
|
|
|
<section id="rest-creating-services">
|
2009-04-09 03:29:02 +08:00
|
|
|
<title>Creating RESTful services</title>
|
|
|
|
|
|
|
|
|
|
<para>Spring's annotation-based MVC framework serves as the basis for
|
|
|
|
|
creating RESTful Web Services. As such, you configure your servlet
|
|
|
|
|
container as you would for a Spring MVC application using Spring's <link
|
2009-04-11 12:49:11 +08:00
|
|
|
linkend="mvc-servlet">DispatcherServlet</link>.</para>
|
2009-04-09 03:29:02 +08:00
|
|
|
|
2009-04-11 13:59:12 +08:00
|
|
|
<section id="rest-uritemplate">
|
2009-04-14 14:59:22 +08:00
|
|
|
<title>URI Templates</title>
|
2009-04-09 03:29:02 +08:00
|
|
|
|
2009-04-14 14:59:22 +08:00
|
|
|
<para>RESTful services use URIs to name resources. To facilitate
|
2009-04-09 03:29:02 +08:00
|
|
|
accessing the information contained in a URI, its structure follows
|
|
|
|
|
conventions so that it can easily be described in a parameterized form.
|
|
|
|
|
The <ulink url="http://bitworking.org/projects/URI-Templates/">proposed
|
2009-04-14 14:59:22 +08:00
|
|
|
RFC</ulink> for URI Templates defines how an URI is parameterized. For
|
2009-04-09 03:29:02 +08:00
|
|
|
example, the URI Template</para>
|
|
|
|
|
|
|
|
|
|
<programlisting>http://www.example.com/users/{userid}</programlisting>
|
|
|
|
|
|
|
|
|
|
<para>contains the variable 'userid'. If we assign the variable the
|
|
|
|
|
value "fred", then 'expanding' the URI Template gives.</para>
|
|
|
|
|
|
|
|
|
|
<programlisting>http://www.example.com/users/fred</programlisting>
|
|
|
|
|
|
2009-04-14 14:59:22 +08:00
|
|
|
<para>When processing a request the URI can be compared to an expected
|
2009-04-11 12:49:11 +08:00
|
|
|
URI Template in order to extract a collection of variables.</para>
|
2009-04-09 03:29:02 +08:00
|
|
|
|
2009-04-14 14:59:22 +08:00
|
|
|
<para>Spring uses the <classname>@RequestMapping</classname> method
|
|
|
|
|
annotation to define the URI Template for the request.
|
2009-04-09 03:29:02 +08:00
|
|
|
The<classname>@PathVariable</classname> annotation is used to extract
|
|
|
|
|
the value of the template variables and assign their value to a method
|
|
|
|
|
variable. A Spring controller method to process above example is shown
|
|
|
|
|
below;</para>
|
|
|
|
|
|
|
|
|
|
<programlisting language="java">@RequestMapping("/users/{userid}", method=RequestMethod.GET)
|
|
|
|
|
public String getUser(@PathVariable String userId) {
|
|
|
|
|
// implementation omitted...
|
|
|
|
|
}</programlisting>
|
|
|
|
|
|
|
|
|
|
<para>The request <literal>http://www.example.com/users/fred</literal>
|
2009-04-11 12:49:11 +08:00
|
|
|
will bind the userId method parameter to the String value 'fred'.</para>
|
2009-04-09 03:29:02 +08:00
|
|
|
|
2009-04-11 13:59:12 +08:00
|
|
|
<section id="rest-path-variable">
|
2009-04-09 03:29:02 +08:00
|
|
|
<title>Mapping RESTful URLs with the @PathVariable annotation</title>
|
|
|
|
|
|
2009-04-14 14:59:22 +08:00
|
|
|
<para>The <classname>@PathVariable</classname> method parameter
|
|
|
|
|
annotation is used to indicate that a method parameter should be bound
|
|
|
|
|
to the value of a URI template variable.</para>
|
2009-04-09 03:29:02 +08:00
|
|
|
|
|
|
|
|
<para>The following code snippet shows the use of a single
|
|
|
|
|
<classname>@PathVariable</classname> in a controller method:</para>
|
|
|
|
|
|
|
|
|
|
<programlisting language="java">@RequestMapping("/owners/{ownerId}", method=RequestMethod.GET)
|
|
|
|
|
public String findOwner(<emphasis role="bold">@PathVariable</emphasis> String ownerId, Model model) {
|
|
|
|
|
Owner owner = ownerService.findOwner(ownerId);
|
|
|
|
|
model.addAttribute("owner", owner);
|
|
|
|
|
return "displayOwner";
|
|
|
|
|
}
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
|
|
<para>The URI Template "<literal>/owners/{ownerId}</literal>"
|
|
|
|
|
specifies the variable name ownerId. When the controller handles this
|
2009-04-14 14:59:22 +08:00
|
|
|
request, the value of ownerId is set the value in the request URI. For
|
|
|
|
|
example, when a request comes in for /owners/fred, the value 'fred' is
|
|
|
|
|
bound to the method parameter <literal>String
|
2009-04-11 12:49:11 +08:00
|
|
|
ownerId</literal>.</para>
|
2009-04-09 03:29:02 +08:00
|
|
|
|
|
|
|
|
<para>The matching of method parameter names to URI Template variable
|
|
|
|
|
names can only be done if your code is compiled with debugging
|
|
|
|
|
enabled. If you do have not debugging enabled, you must specify the
|
|
|
|
|
name of the URI Template variable name to bind to in the @PathVariable
|
|
|
|
|
annotation. For example</para>
|
|
|
|
|
|
2009-04-11 12:49:11 +08:00
|
|
|
<programlisting language="java">@RequestMapping("/owners/{ownerId}", method=RequestMethod.GET)
|
2009-04-09 03:29:02 +08:00
|
|
|
public String findOwner(<emphasis role="bold">@PathVariable</emphasis>("ownerId") String ownerId, Model model) {
|
|
|
|
|
// implementation omitted
|
|
|
|
|
}
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
|
|
<para>The name of the method parameter does not matter in this case,
|
2009-04-14 14:59:22 +08:00
|
|
|
so you may also use a controller method with the signature shown
|
|
|
|
|
below</para>
|
2009-04-09 03:29:02 +08:00
|
|
|
|
2009-04-11 12:49:11 +08:00
|
|
|
<programlisting language="java">@RequestMapping("/owners/{ownerId}", method=RequestMethod.GET)
|
2009-04-09 03:29:02 +08:00
|
|
|
public String findOwner(<emphasis role="bold">@PathVariable</emphasis>("ownerId") String theOwner, Model model) {
|
|
|
|
|
// implementation omitted
|
|
|
|
|
}</programlisting>
|
|
|
|
|
|
|
|
|
|
<para>Multiple @PathVariable annotations can be used to bind to
|
|
|
|
|
multiple URI Template variables as shown below:</para>
|
|
|
|
|
|
|
|
|
|
<programlisting language="java">@RequestMapping("/owners/{ownerId}/pets/{petId}", method=RequestMethod.GET)
|
|
|
|
|
public String findPet(<emphasis role="bold">@PathVariable</emphasis> String ownerId, <emphasis
|
|
|
|
|
role="bold">@PathVariable</emphasis> String petId, Model model) {
|
|
|
|
|
Owner owner = ownerService.findOwner(ownderId);
|
|
|
|
|
Pet pet = owner.getPet(petId);
|
|
|
|
|
model.addAttribute("pet", pet);
|
|
|
|
|
return "displayPet";
|
|
|
|
|
}
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
|
|
<para>The following code snippet shows the use of path variables on a
|
|
|
|
|
relative path</para>
|
|
|
|
|
|
|
|
|
|
<programlisting language="java">@Controller
|
|
|
|
|
@RequestMapping(<emphasis role="bold">"/owners/{ownerId}/**"</emphasis>)
|
|
|
|
|
public class RelativePathUriTemplateController {
|
|
|
|
|
|
|
|
|
|
@RequestMapping(<emphasis role="bold">"/pets/{petId}"</emphasis>)
|
|
|
|
|
public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
|
|
|
|
|
// implementation omitted
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
|
|
<tip>
|
|
|
|
|
<para>method parameters that are decorated with the
|
|
|
|
|
<interfacename>@PathVariable</interfacename> annotation can be of
|
|
|
|
|
<emphasis role="bold">any simple type </emphasis>such as int, long,
|
|
|
|
|
Date... Spring automatically converts to the appropriate type and
|
|
|
|
|
throws a <classname>TypeMismatchException</classname> if the type is
|
|
|
|
|
not correct.</para>
|
|
|
|
|
</tip>
|
|
|
|
|
</section>
|
2009-04-14 14:59:22 +08:00
|
|
|
|
|
|
|
|
<section>
|
|
|
|
|
<title>Mapping the request body with the @RequestBody
|
|
|
|
|
annotation</title>
|
|
|
|
|
|
|
|
|
|
<para>The <classname>@RequestBody</classname> method parameter
|
|
|
|
|
annotation is used to indicate that a method parameter should be bound
|
|
|
|
|
to the value of the HTTP request body. For example, </para>
|
|
|
|
|
|
|
|
|
|
<programlisting language="java">@RequestMapping(value = "/something", method = RequestMethod.PUT)
|
|
|
|
|
public void handle(@RequestBody String body, Writer writer) throws IOException {
|
|
|
|
|
writer.write(body);
|
|
|
|
|
}</programlisting>
|
|
|
|
|
|
|
|
|
|
<para>The conversion of the request body to the method argument is
|
|
|
|
|
done using a <interfacename>HttpMessageConverter</interfacename>.
|
|
|
|
|
<interfacename>HttpMessageConverter</interfacename> is responsible for
|
|
|
|
|
converting for converting from the HTTP request message to an object
|
|
|
|
|
and converting from an object to the HTTP response body.
|
|
|
|
|
<classname>DispatcherServlet</classname> supports annotation based
|
|
|
|
|
processing using the
|
|
|
|
|
<classname>DefaultAnnotationHandlerMapping</classname> and
|
|
|
|
|
<classname>AnnotationMethodHandlerAdapter</classname>. In Spring 3 the
|
|
|
|
|
<classname>AnnotationMethodHandlerAdapter</classname> has been
|
|
|
|
|
extended to support the <classname>@RequestBody</classname> and has
|
|
|
|
|
several <interfacename>HttpMessageConverters</interfacename>
|
|
|
|
|
registered by default, these are</para>
|
|
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para><classname>ByteArrayHttpMessageConverter</classname> -
|
|
|
|
|
converts byte arrays</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
|
<para><classname>StringHttpMessageConverter</classname> - converts
|
|
|
|
|
strings</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
|
<para><classname>FormHttpMessageConverter</classname> - converts
|
|
|
|
|
form data to/from a MultiValueMap<String, String></para>
|
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
|
<para><classname>SourceHttpMessageConverter</classname> - convert
|
|
|
|
|
to/from a javax.xml.transform.Source;</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
|
<para><classname>MarshallingHttpMessageConverter</classname> -
|
|
|
|
|
convert to/from an object using the
|
|
|
|
|
<classname>org.springframework.oxm</classname> package.</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
|
|
|
|
<para>More information on these converters can be found in the section
|
|
|
|
|
<link linkend="rest-message-conversion">Message Converters</link>.
|
|
|
|
|
</para>
|
|
|
|
|
|
|
|
|
|
<para>The <classname>MarshallingHttpMessageConverter</classname>
|
|
|
|
|
requires a <interfacename>Marshaller</interfacename> and
|
|
|
|
|
<interfacename>Unmarshaller</interfacename> from the
|
|
|
|
|
<classname>org.springframework.oxm</classname> package to be
|
|
|
|
|
configured on an instance of
|
|
|
|
|
<classname>AnnotationMethodHandlerAdapter</classname> in the
|
|
|
|
|
application context. For example</para>
|
|
|
|
|
|
|
|
|
|
<programlisting language="xml"><bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
|
|
|
|
|
<property name="messageConverters">
|
|
|
|
|
<util:list id="beanList">
|
|
|
|
|
<ref bean="stringHttpMessageConverter"/>
|
|
|
|
|
<ref bean="marshallingHttpMessageConverter"/>
|
|
|
|
|
</util:list>
|
|
|
|
|
</property
|
|
|
|
|
</bean>
|
|
|
|
|
|
|
|
|
|
<bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter"/>
|
|
|
|
|
|
|
|
|
|
<bean id="marshallingHttpMessageConverter" class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
|
|
|
|
|
<property name="marshaller" ref="castorMarshaller" />
|
|
|
|
|
<property name="unmarshaller" ref="castorMarshaller" />
|
|
|
|
|
</bean>
|
|
|
|
|
|
|
|
|
|
<bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller"/>
|
|
|
|
|
</programlisting>
|
|
|
|
|
</section>
|
2009-04-09 03:29:02 +08:00
|
|
|
</section>
|
|
|
|
|
|
2009-04-11 13:59:12 +08:00
|
|
|
<section id="rest-multiple-representations">
|
2009-04-11 12:49:11 +08:00
|
|
|
<title>Returning multiple representations</title>
|
|
|
|
|
|
|
|
|
|
<para>A RESTful architecture may expose multiple representations of a
|
|
|
|
|
resource. There are two strategies for a client to inform the server of
|
|
|
|
|
the representation it is interested in receiving.</para>
|
|
|
|
|
|
|
|
|
|
<para>The first strategy is to use a distinct URI for each resource.
|
|
|
|
|
This is typically done by using a different file extension in the URI.
|
|
|
|
|
For example the URI<literal>
|
|
|
|
|
http://www.example.com/users/fred.pdf</literal> requests a PDF
|
|
|
|
|
representation of the user fred while
|
|
|
|
|
<literal>http://www.example.com/users/fred.xml</literal> requests an XML
|
|
|
|
|
representation.</para>
|
|
|
|
|
|
|
|
|
|
<para>The second strategy is for the client to use the same URI to
|
|
|
|
|
locate the resource but set the <literal>Accept</literal> HTTP request
|
|
|
|
|
header to list the <ulink
|
|
|
|
|
url="http://en.wikipedia.org/wiki/Internet_media_type">media
|
|
|
|
|
types</ulink> that it understands. For example, a HTTP request for
|
|
|
|
|
<literal>http://www.example.com/users/fred</literal> with an
|
|
|
|
|
<literal>Accept</literal> header set to <literal>application/pdf
|
|
|
|
|
</literal>requests a PDF representation of the user fred while
|
|
|
|
|
<literal>http://www.example.com/users/fred</literal> with an
|
|
|
|
|
<literal>Accept</literal> header set to <literal>text/xml</literal>
|
|
|
|
|
requests an XML representation. This strategy is known as <ulink
|
|
|
|
|
url="http://en.wikipedia.org/wiki/Content_negotiation">content
|
|
|
|
|
negotiation</ulink>.</para>
|
|
|
|
|
|
|
|
|
|
<note>
|
|
|
|
|
<para>One issue with the Accept header is that is impossible to change
|
|
|
|
|
it in a web browser, in HTML. For instance, in Firefox, it's fixed
|
|
|
|
|
to</para>
|
|
|
|
|
|
|
|
|
|
<programlisting>Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
|
|
<para>For this reason it is common to see the use of a distinct URI
|
|
|
|
|
for each representation.</para>
|
|
|
|
|
</note>
|
|
|
|
|
|
|
|
|
|
<para>To support multiple representations of a resource Spring provides
|
|
|
|
|
the <classname>ContentNegotiatingViewResolver</classname> to resolve a
|
|
|
|
|
view based on the file extension or <literal>Accept</literal> header of
|
|
|
|
|
the HTTP request. <classname>ContentNegotiatingViewResolver</classname>
|
|
|
|
|
does not perform the view resolution itself, but instead delegates to a
|
2009-04-14 14:59:22 +08:00
|
|
|
list of view resolvers set using the bean property
|
2009-04-11 12:49:11 +08:00
|
|
|
<literal>ViewResolvers</literal>.</para>
|
|
|
|
|
|
|
|
|
|
<para>The <classname>ContentNegotiatingViewResolver</classname> selects
|
|
|
|
|
an appropriate <classname>View</classname> to handle the request by
|
|
|
|
|
comparing the request media type(s) with the media type (a.k.a.
|
|
|
|
|
<literal>Content-Type</literal>) supported by the
|
|
|
|
|
<classname>View</classname> associated with each of its
|
|
|
|
|
<classname>ViewResolvers</classname>. The first
|
|
|
|
|
<classname>View</classname> in the list that has a compatible
|
|
|
|
|
<literal>Content-Type</literal> is used to return the representation to
|
|
|
|
|
the client. The <literal>Accept</literal> header may include wild cards,
|
|
|
|
|
for example 'text/*', in which case a <classname>View</classname> whose
|
|
|
|
|
Context-Type was 'text/xml' is a compatible match.</para>
|
|
|
|
|
|
|
|
|
|
<para>To support the resolution of a view based on a file extension,
|
2009-04-14 14:59:22 +08:00
|
|
|
<classname>ContentNegotiatingViewResolver</classname>'s bean property
|
2009-04-11 12:49:11 +08:00
|
|
|
<literal>MediaTypes</literal> is used to specify a mapping of file
|
|
|
|
|
extensions to media types. For more information on the algorithm to
|
|
|
|
|
determine the request media type, refer to the API documentation for
|
|
|
|
|
<classname>ContentNegotiatingViewResolver</classname>..</para>
|
|
|
|
|
|
|
|
|
|
<para>Here is an example configuration of a
|
|
|
|
|
<classname>ContentNegotiatingViewResolver</classname></para>
|
|
|
|
|
|
2009-04-14 01:46:21 +08:00
|
|
|
<programlisting language="xml"> <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
|
|
|
|
|
<property name="mediaTypes">
|
|
|
|
|
<map>
|
|
|
|
|
<entry key="atom" value="application/atom+xml"/>
|
|
|
|
|
<entry key="html" value="text/html"/>
|
|
|
|
|
</map>
|
|
|
|
|
</property>
|
|
|
|
|
<property name="viewResolvers">
|
|
|
|
|
<list>
|
|
|
|
|
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
|
|
|
|
|
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
|
|
|
|
|
<property name="prefix" value="/WEB-INF/jsp/"/>
|
|
|
|
|
<property name="suffix" value=".jsp"/>
|
|
|
|
|
</bean>
|
|
|
|
|
</list>
|
|
|
|
|
</property>
|
|
|
|
|
</bean>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<bean id="content" class="com.springsource.samples.rest.SampleContentAtomView"/></programlisting>
|
2009-04-11 12:49:11 +08:00
|
|
|
|
|
|
|
|
<para>The <classname>InternalResourceViewResolver</classname> handles
|
|
|
|
|
the translation of view names and JSP pages while the
|
|
|
|
|
<classname>BeanNameViewResolver</classname> returns a view based on the
|
|
|
|
|
name of a bean. (See "<link
|
|
|
|
|
linkend="mvc-viewresolver-resolver">Resolving views - the ViewResolver
|
|
|
|
|
interface</link>" for more details on how Spring looks up and
|
|
|
|
|
instantiates a view.) In this example, the <literal>content</literal>
|
|
|
|
|
bean is a class that inherits from
|
|
|
|
|
<classname>AbstractAtomFeedView</classname> which returns an Atom RSS
|
|
|
|
|
feed. For more information on creating an Atom Feed representation see
|
2009-04-14 06:11:15 +08:00
|
|
|
the section 'Atom Views'.</para>
|
2009-04-11 12:49:11 +08:00
|
|
|
|
|
|
|
|
<para>In this configuration, if a request is made with a .html extension
|
|
|
|
|
the view resolver will look for a view that matches the text/html media
|
|
|
|
|
type. The <classname>InternalResourceViewResolver</classname> provides
|
|
|
|
|
the matching view for text/html. If the request is made with the file
|
|
|
|
|
extension .atom, the view resolver will look for a view that matches the
|
|
|
|
|
application/atom+xml media type. This view is provided by the
|
|
|
|
|
<classname>BeanNameViewResolver</classname> that maps to the
|
|
|
|
|
<classname>SampleContentAtomView</classname> if the view name returned
|
|
|
|
|
is 'content'. Alternatively, client requests could be made without a
|
|
|
|
|
file extension and setting the Accept header to the preferred media-type
|
2009-04-11 13:59:12 +08:00
|
|
|
and the same resolution of request to views would occur.</para>
|
2009-04-11 12:49:11 +08:00
|
|
|
|
|
|
|
|
<note>
|
|
|
|
|
<para>If <classname>ContentNegotiatingViewResolver</classname>'s list
|
|
|
|
|
of ViewResolvers is not configured explicitly, then it will
|
|
|
|
|
automatically use any ViewResolvers defined in the application
|
|
|
|
|
context.</para>
|
|
|
|
|
</note>
|
|
|
|
|
|
|
|
|
|
<para>The corresponding controller code that returns an Atom RSS feed
|
|
|
|
|
for a URI of the form <literal>http://localhost/content.atom</literal>
|
|
|
|
|
or <literal>http://localhost/content</literal> with an
|
|
|
|
|
<literal>Accept</literal> header of application/atom+xml is shown
|
|
|
|
|
below</para>
|
|
|
|
|
|
|
|
|
|
<programlisting language="java">@Controller
|
|
|
|
|
public class ContentController {
|
2009-04-14 01:46:21 +08:00
|
|
|
|
|
|
|
|
private List<SampleContent> contentList = new ArrayList<SampleContent>();
|
|
|
|
|
|
|
|
|
|
@RequestMapping(value="/content", method=RequestMethod.GET)
|
|
|
|
|
public ModelAndView getContent() {
|
|
|
|
|
ModelAndView mav = new ModelAndView();
|
|
|
|
|
mav.setViewName("content");
|
|
|
|
|
mav.addObject("sampleContentList", contentList);
|
|
|
|
|
return mav;
|
|
|
|
|
}
|
2009-04-09 03:29:02 +08:00
|
|
|
|
2009-04-11 12:49:11 +08:00
|
|
|
}</programlisting>
|
|
|
|
|
|
|
|
|
|
<para></para>
|
2009-04-09 03:29:02 +08:00
|
|
|
</section>
|
|
|
|
|
|
2009-04-11 13:59:12 +08:00
|
|
|
<section id="rest-views">
|
2009-04-09 03:29:02 +08:00
|
|
|
<title>Views</title>
|
|
|
|
|
|
2009-04-14 14:59:22 +08:00
|
|
|
<para>Several views were added in Spring 3 to help support creating
|
2009-04-14 06:11:15 +08:00
|
|
|
RESTful services. They are:</para>
|
|
|
|
|
|
|
|
|
|
<itemizedlist>
|
|
|
|
|
<listitem>
|
|
|
|
|
<para><classname>AbstractAtomFeedView</classname> - return an Atom
|
|
|
|
|
feed</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
|
<para><classname>AbstractRssFeedView</classname> - returns a RSS
|
|
|
|
|
feed</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
|
|
|
|
|
<listitem>
|
|
|
|
|
<para><classname>MarshallingView</classname> - returns an XML
|
2009-04-14 14:59:22 +08:00
|
|
|
representation using Spring's Object to XML mapping (OXM)
|
2009-04-14 06:11:15 +08:00
|
|
|
functionality</para>
|
|
|
|
|
</listitem>
|
|
|
|
|
</itemizedlist>
|
|
|
|
|
|
2009-04-14 14:59:22 +08:00
|
|
|
<note>
|
|
|
|
|
<para>Available separately is the
|
|
|
|
|
<classname>JacksonJsonView</classname> included as part of the Spring
|
|
|
|
|
JavaScript project.</para>
|
|
|
|
|
</note>
|
2009-04-14 06:11:15 +08:00
|
|
|
|
|
|
|
|
<section id="rest-feedview">
|
|
|
|
|
<title>Feed Views</title>
|
|
|
|
|
|
|
|
|
|
<para>Both <classname>AbstractAtomFeedView</classname> and
|
|
|
|
|
<classname>AbstractRssFeedView</classname> inherit from the base class
|
2009-04-14 14:59:22 +08:00
|
|
|
<classname>AbstractFeedView</classname> and are used to provide Atom
|
|
|
|
|
and RSS Feed views respectfully. They are based on java.net's <ulink
|
|
|
|
|
url="https://rome.dev.java.net">ROME</ulink> project and are located
|
|
|
|
|
in the package
|
|
|
|
|
<literal>org.springframework.web.servlet.view.feed</literal>. </para>
|
|
|
|
|
|
|
|
|
|
<para><classname>AbstractAtomFeedView</classname> requires you to
|
2009-04-14 06:11:15 +08:00
|
|
|
implement the <methodname>buildFeedEntries</methodname> method and
|
|
|
|
|
optionally override the <methodname>buildFeedMetadata</methodname>
|
|
|
|
|
method (the default implementation is empty), as shown below</para>
|
|
|
|
|
|
2009-04-14 14:59:22 +08:00
|
|
|
<programlisting language="java">public class SampleContentAtomView extends AbstractAtomFeedView {
|
2009-04-14 06:11:15 +08:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void buildFeedMetadata(Map<String, Object> model, Feed feed, HttpServletRequest request) {
|
|
|
|
|
// implementation omitted
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected List<Entry> buildFeedEntries(Map<String, Object> model,
|
|
|
|
|
HttpServletRequest request, HttpServletResponse response) throws Exception {
|
|
|
|
|
|
|
|
|
|
// implementation omitted
|
|
|
|
|
}
|
|
|
|
|
}</programlisting>
|
|
|
|
|
|
|
|
|
|
<para>Similar requirements apply for implementing
|
|
|
|
|
<classname>AbstractRssFeedView</classname>, as shown below</para>
|
|
|
|
|
|
2009-04-14 14:59:22 +08:00
|
|
|
<programlisting language="java">public class SampleContentAtomView extends AbstractRssFeedView {
|
2009-04-14 06:11:15 +08:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void buildFeedMetadata(Map<String, Object> model, Channel feed, HttpServletRequest request) {
|
|
|
|
|
// implementation omitted
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected List<Item> buildFeedItems(Map<String, Object> model,
|
|
|
|
|
HttpServletRequest request, HttpServletResponse response) throws Exception {
|
|
|
|
|
// implementation omitted
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}</programlisting>
|
|
|
|
|
|
|
|
|
|
<para>The <methodname>buildFeedItems</methodname> and
|
|
|
|
|
<methodname>buildFeedEntires</methodname> pass in the HTTP request in
|
|
|
|
|
case you need to access the Locale. The HTTP response in passed in
|
|
|
|
|
only for the setting of cookies or other HTTP headers. The feed will
|
|
|
|
|
automatically be written to the response object after the method
|
|
|
|
|
returns. </para>
|
|
|
|
|
|
2009-04-14 14:59:22 +08:00
|
|
|
<para>For an example of creating a Atom view please refer to Alef
|
|
|
|
|
Arendsen's SpringSource TeamBlog <ulink
|
2009-04-14 06:11:15 +08:00
|
|
|
url="http://blog.springsource.com/2009/03/16/adding-an-atom-view-to-an-application-using-springs-rest-support/">entry</ulink>.</para>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section>
|
|
|
|
|
<title>XML Marshalling View</title>
|
|
|
|
|
|
2009-04-14 14:59:22 +08:00
|
|
|
<para>The <classname>MarhsallingView</classname> uses a XML
|
|
|
|
|
<interfacename>Marshaller</interfacename> defined in the
|
|
|
|
|
<classname>org.springframework.oxm</classname> package to render the
|
|
|
|
|
response content as XML. The object to be marshalled can be set
|
|
|
|
|
explicitly using <classname>MarhsallingView</classname>'s
|
|
|
|
|
<property>modelKey</property> bean property. Alternatively, the view
|
|
|
|
|
will iterate over all model properties marhsall only those types that
|
|
|
|
|
are supported by the <interfacename>Marshaller</interfacename>.</para>
|
2009-04-14 06:11:15 +08:00
|
|
|
</section>
|
2009-04-09 03:29:02 +08:00
|
|
|
</section>
|
|
|
|
|
|
2009-04-11 13:59:12 +08:00
|
|
|
<section id="rest-method-conversion">
|
2009-04-09 03:29:02 +08:00
|
|
|
<title>HTTP Method Conversion</title>
|
|
|
|
|
|
2009-04-11 13:59:12 +08:00
|
|
|
<para>A key principle of REST is the use of the Uniform Interface. This
|
|
|
|
|
means that all resources (URLs) can be manipulated using the same four
|
|
|
|
|
HTTP method: GET, PUT, POST, and DELETE. For each methods, the HTTP
|
|
|
|
|
specification defines the exact semantics. For instance, a GET should
|
2009-04-11 12:49:11 +08:00
|
|
|
always be a safe operation, meaning that is has no side effects, and a
|
|
|
|
|
PUT or DELETE should be idempotent, meaning that you can repeat these
|
|
|
|
|
operations over and over again, but the end result should be the same.
|
|
|
|
|
While HTTP defines these four methods, HTML only supports two: GET and
|
|
|
|
|
POST. Fortunately, there are two possible workarounds: you can either
|
|
|
|
|
use JavaScript to do your PUT or DELETE, or simply do a POST with the
|
|
|
|
|
'real' method as an additional parameter (modeled as a hidden input
|
2009-04-11 13:59:12 +08:00
|
|
|
field in an HTML form). This latter trick is what Spring's
|
|
|
|
|
<classname>HiddenHttpMethodFilter</classname> does. This filter is a
|
|
|
|
|
plain Servlet Filter and therefore it can be used in combination with
|
|
|
|
|
any web framework (not just Spring MVC). Simply add this filter to your
|
|
|
|
|
web.xml, and a POST with a hidden _method parameter will be converted
|
|
|
|
|
into the corresponding HTTP method request.</para>
|
|
|
|
|
|
|
|
|
|
<section id="rest-form-tags">
|
2009-04-11 12:49:11 +08:00
|
|
|
<title>Supporting Spring form tags</title>
|
|
|
|
|
|
|
|
|
|
<para>To support HTTP method conversion the Spring MVC form tag was
|
|
|
|
|
updated to support setting the HTTP method. For example, the following
|
|
|
|
|
snippet taken from the updated Petclinic sample</para>
|
|
|
|
|
|
2009-04-14 01:46:21 +08:00
|
|
|
<programlisting language="xml"><form:form method="delete">
|
2009-04-11 12:49:11 +08:00
|
|
|
<p class="submit"><input type="submit" value="Delete Pet"/></p>
|
|
|
|
|
</form:form></programlisting>
|
|
|
|
|
|
|
|
|
|
<para>This will actually perform an HTTP POST, with the 'real' DELETE
|
|
|
|
|
method hidden behind a request parameter, to be picked up by the
|
|
|
|
|
<classname>HiddenHttpMethodFilter</classname>. The corresponding
|
|
|
|
|
@Controller method is shown below</para>
|
|
|
|
|
|
|
|
|
|
<programlisting language="java">@RequestMapping(method = RequestMethod.DELETE)
|
|
|
|
|
public String deletePet(@PathVariable int ownerId, @PathVariable int petId) {
|
|
|
|
|
this.clinic.deletePet(petId);
|
|
|
|
|
return "redirect:/owners/" + ownerId;
|
|
|
|
|
}</programlisting>
|
|
|
|
|
</section>
|
2009-04-09 03:29:02 +08:00
|
|
|
</section>
|
|
|
|
|
|
2009-04-11 13:59:12 +08:00
|
|
|
<section id="rest-etag">
|
2009-04-09 03:29:02 +08:00
|
|
|
<title>ETag support</title>
|
|
|
|
|
|
2009-04-11 12:49:11 +08:00
|
|
|
<para>An <ulink
|
|
|
|
|
url="http://en.wikipedia.org/wiki/HTTP_ETag">ETag</ulink> (entity tag)
|
|
|
|
|
is an HTTP response header returned by an HTTP/1.1 compliant web server
|
|
|
|
|
used to determine change in content at a given URL. It can be considered
|
|
|
|
|
to be the more sophisticated successor to the
|
|
|
|
|
<literal>Last-Modified</literal> header. When a server returns a
|
|
|
|
|
representation with an ETag header, client can use this header in
|
|
|
|
|
subsequent GETs, in a <literal>If-None-Match</literal> header. If the
|
|
|
|
|
content has not changed, the server will return <literal>304: Not
|
|
|
|
|
Modified</literal>.</para>
|
|
|
|
|
|
|
|
|
|
<para>Support for ETags is provided by the servlet filter
|
|
|
|
|
<classname>ShallowEtagHeaderFilter</classname>. Since it is a plain
|
|
|
|
|
Servlet Filter, and thus can be used in combination any web framework.
|
|
|
|
|
The <classname>ShallowEtagHeaderFilter</classname> filter creates
|
|
|
|
|
so-called shallow ETags (as opposed to a deep ETags, more about that
|
|
|
|
|
later). The way it works is quite simple: the filter simply caches the
|
|
|
|
|
content of the rendered JSP (or other content), generates a MD5 hash
|
|
|
|
|
over that, and returns that as a ETag header in the response. The next
|
|
|
|
|
time a client sends a request for the same resource, it use that hash as
|
|
|
|
|
the <literal>If-None-Match</literal> value. The filter notices this,
|
|
|
|
|
renders the view again, and compares the two hashes. If they are equal,
|
|
|
|
|
a <literal>304</literal> is returned. It is important to note that this
|
|
|
|
|
filter will not save processing power, as the view is still rendered.
|
2009-04-14 14:59:22 +08:00
|
|
|
The only thing it saves is bandwidth, as the rendered response is not
|
2009-04-11 12:49:11 +08:00
|
|
|
sent back over the wire.</para>
|
|
|
|
|
|
|
|
|
|
<para>Deep ETags are a bit more complicated. In this case, the ETag is
|
|
|
|
|
based on the underlying domain objects, RDMBS tables, etc. Using this
|
|
|
|
|
approach, no content is generated unless the underlying data has
|
|
|
|
|
changed. Unfortunately, implementing this approach in a generic way is
|
|
|
|
|
much more difficult than shallow ETags. Spring may provide support for
|
|
|
|
|
deep ETags in a later release by relying on JPA's @Version annotation,
|
|
|
|
|
or an AspectJ aspect.</para>
|
2009-04-09 03:29:02 +08:00
|
|
|
</section>
|
|
|
|
|
|
2009-04-11 13:59:12 +08:00
|
|
|
<section id="rest-exception">
|
2009-04-09 03:29:02 +08:00
|
|
|
<title>Exception Handling</title>
|
|
|
|
|
|
2009-04-14 14:59:22 +08:00
|
|
|
<para>The <classname>@ExceptionHandling</classname> method annotation is
|
|
|
|
|
used within a controller to specify which method will be invoked when an
|
|
|
|
|
exception of a specific type is thrown during the execution of
|
|
|
|
|
controller methods. For example</para>
|
|
|
|
|
|
|
|
|
|
<programlisting language="java">@Controller
|
|
|
|
|
public class SimpleController {
|
|
|
|
|
|
|
|
|
|
// other controller method omitted
|
|
|
|
|
|
|
|
|
|
@ExceptionHandler(IOException.class)
|
|
|
|
|
public String handleIOException(IOException ex, HttpServletRequest request) {
|
|
|
|
|
return ClassUtils.getShortName(ex.getClass());
|
|
|
|
|
}
|
|
|
|
|
}</programlisting>
|
|
|
|
|
|
|
|
|
|
<para>will invoke the 'handlerIOException' method when a
|
|
|
|
|
<classname>java.io.IOException</classname> is thrown.</para>
|
|
|
|
|
|
|
|
|
|
<para>The <classname>@ExceptionHandler</classname> value can be set to
|
|
|
|
|
an array of Exception types. If an exception is thrown matches one of
|
|
|
|
|
the types in the list, then the method annotated with the matching
|
|
|
|
|
<classname>@ExceptionHandler</classname> will be invoked. If the
|
|
|
|
|
annotation value is not set then the exception types listed as method
|
|
|
|
|
arguments are used. </para>
|
|
|
|
|
|
|
|
|
|
<para>Much like standard controller methods annotated with a
|
|
|
|
|
<classname>@RequestMapping</classname> annotation, the method arguments
|
|
|
|
|
and return values of <classname>@ExceptionHandler</classname> methods
|
|
|
|
|
are very flexible. For example, the
|
|
|
|
|
<classname>HttpServletRequest</classname> can be access in Servlet
|
|
|
|
|
environments and the <classname>PortletRequest</classname> in Portlet
|
|
|
|
|
environments. The return type can be a <classname>String</classname>,
|
|
|
|
|
which is interpreted as a view name or a
|
|
|
|
|
<classname>ModelAndView</classname> object. Please refer to the API
|
|
|
|
|
documentation for more details. </para>
|
2009-04-09 03:29:02 +08:00
|
|
|
</section>
|
|
|
|
|
</section>
|
|
|
|
|
|
2009-04-11 13:59:12 +08:00
|
|
|
<section id="rest-client-access">
|
2009-04-09 03:29:02 +08:00
|
|
|
<title>Accessing RESTful services on the Client</title>
|
|
|
|
|
|
2009-04-14 14:59:22 +08:00
|
|
|
<para>The <classname>RestTemplate</classname> is the core class for
|
|
|
|
|
client-side access to RESTful services. It is conceptually similar to
|
|
|
|
|
other template classes in Spring, such as
|
|
|
|
|
<classname>JdbcTemplate</classname> and <classname>JmsTemplate</classname>
|
|
|
|
|
and other template classes found in other Spring portfolio projects.
|
|
|
|
|
<classname>RestTemplate</classname>'s behavior is customized by providing
|
|
|
|
|
callback methods and configuring the
|
|
|
|
|
<interfacename>HttpMessageConverter</interfacename> used to marshal
|
|
|
|
|
objects into the HTTP request body and to unmarshall any response back
|
|
|
|
|
into an object. As it is common to use XML as a message format, Spring
|
|
|
|
|
provides a <classname>MarshallingHttpMessageConverter</classname> that
|
|
|
|
|
uses the Object-to-XML framework that is part of the
|
|
|
|
|
<classname>org.springframework.oxm</classname> package. This gives you a
|
|
|
|
|
wide range of choices of XML to Object mapping technologies to choose
|
|
|
|
|
from. </para>
|
|
|
|
|
|
|
|
|
|
<para>This section describes how to use the
|
|
|
|
|
<classname>RestTemplate</classname> and its associated
|
|
|
|
|
<interfacename>HttpMessageConverters</interfacename>.</para>
|
2009-04-09 03:29:02 +08:00
|
|
|
|
2009-04-11 13:59:12 +08:00
|
|
|
<section id="rest-resttemplate">
|
2009-04-09 03:29:02 +08:00
|
|
|
<title>RestTemplate</title>
|
|
|
|
|
|
2009-04-14 14:59:22 +08:00
|
|
|
<para>Invoking RESTful services in Java is typically done using a helper
|
|
|
|
|
class such as Jakarta Commons <classname>HttpClient</classname>. For
|
|
|
|
|
common REST operations this approach is too low level as shown
|
|
|
|
|
below.</para>
|
|
|
|
|
|
|
|
|
|
<programlisting>String uri = "http://example.com/hotels/1/bookings";
|
|
|
|
|
|
|
|
|
|
PostMethod post = new PostMethod(uri);
|
|
|
|
|
String request = // create booking request content
|
|
|
|
|
post.setRequestEntity(new StringRequestEntity(request));
|
|
|
|
|
|
|
|
|
|
httpClient.executeMethod(post);
|
|
|
|
|
|
|
|
|
|
if (HttpStatus.SC_CREATED == post.getStatusCode()) {
|
|
|
|
|
Header location = post.getRequestHeader("Location");
|
|
|
|
|
if (location != null) {
|
|
|
|
|
System.out.println("Created new booking at :" + location.getValue());
|
|
|
|
|
}
|
|
|
|
|
}</programlisting>
|
|
|
|
|
|
|
|
|
|
<para> RestTemplate provides higher level methods that correspond to
|
|
|
|
|
each of the six main HTTP methods that make invoking many RESTful
|
|
|
|
|
services a one-liner and enforce REST best practices.</para>
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<title>Overview of RestTemplate methods</title>
|
|
|
|
|
|
|
|
|
|
<tgroup cols="2">
|
|
|
|
|
<tbody>
|
|
|
|
|
<row>
|
|
|
|
|
<entry><emphasis role="bold">HTTP Method</emphasis></entry>
|
|
|
|
|
|
|
|
|
|
<entry><emphasis role="bold">RestTemplate
|
|
|
|
|
Method</emphasis></entry>
|
|
|
|
|
</row>
|
|
|
|
|
|
|
|
|
|
<row>
|
|
|
|
|
<entry>DELETE</entry>
|
|
|
|
|
|
|
|
|
|
<entry><ulink
|
|
|
|
|
url="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/client/RestTemplate.html#delete(String,%20String...)">delete(String
|
|
|
|
|
url, String… urlVariables)</ulink></entry>
|
|
|
|
|
</row>
|
|
|
|
|
|
|
|
|
|
<row>
|
|
|
|
|
<entry>GET</entry>
|
|
|
|
|
|
|
|
|
|
<entry><ulink
|
|
|
|
|
url="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/client/RestTemplate.html#getForObject(String,%20Class,%20String...)">getForObject(String
|
|
|
|
|
url, Class<T> responseType, String…
|
|
|
|
|
urlVariables)</ulink></entry>
|
|
|
|
|
</row>
|
|
|
|
|
|
|
|
|
|
<row>
|
|
|
|
|
<entry>HEAD</entry>
|
|
|
|
|
|
|
|
|
|
<entry><ulink
|
|
|
|
|
url="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/client/RestTemplate.html#headForHeaders(String,%20String...)">headForHeaders(String
|
|
|
|
|
url, String… urlVariables)</ulink></entry>
|
|
|
|
|
</row>
|
|
|
|
|
|
|
|
|
|
<row>
|
|
|
|
|
<entry>OPTIONS</entry>
|
|
|
|
|
|
|
|
|
|
<entry><ulink
|
|
|
|
|
url="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/client/RestTemplate.html#optionsForAllow(String,%20String...)">optionsForAllow(String
|
|
|
|
|
url, String… urlVariables)</ulink></entry>
|
|
|
|
|
</row>
|
|
|
|
|
|
|
|
|
|
<row>
|
|
|
|
|
<entry>POST</entry>
|
|
|
|
|
|
|
|
|
|
<entry><ulink
|
|
|
|
|
url="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/client/RestTemplate.html#postForLocation(String,%20Object,%20String...)">postForLocation(String
|
|
|
|
|
url, Object request, String… urlVariables)</ulink></entry>
|
|
|
|
|
</row>
|
|
|
|
|
|
|
|
|
|
<row>
|
|
|
|
|
<entry>PUT</entry>
|
|
|
|
|
|
|
|
|
|
<entry><ulink
|
|
|
|
|
url="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/client/RestTemplate.html#put(String,%20Object,%20String...)">put(String
|
|
|
|
|
url, Object request, String…urlVariables)</ulink></entry>
|
|
|
|
|
</row>
|
|
|
|
|
</tbody>
|
|
|
|
|
</tgroup>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
<para>The names of <classname>RestTemplate</classname> methods follow a
|
|
|
|
|
naming convention, the first part indicates what HTTP method is being
|
|
|
|
|
invoked and the second part indicates what is returned. For example, the
|
|
|
|
|
method <methodname>getForObject</methodname> will perform a GET, convert
|
|
|
|
|
the HTTP response into an object type of your choice, and returns that
|
|
|
|
|
object. The method <methodname>postForLocation</methodname> will do a
|
|
|
|
|
POST, converting the given object into a HTTP request, and returns the
|
|
|
|
|
response HTTP Location header where the newly created object can be
|
|
|
|
|
found In case of an exception processing the HTTP request, an exception
|
|
|
|
|
of the type <classname>RestClientException</classname> will be
|
|
|
|
|
thrown.</para>
|
|
|
|
|
|
|
|
|
|
<para>Objects passed to and returned from these methods are converted to
|
|
|
|
|
and from HTTP messages by
|
|
|
|
|
<interfacename>HttpMessageConverter</interfacename> instances.
|
|
|
|
|
Converters for the main mime types are registered by default, but you
|
|
|
|
|
can also write your own converter and register it via the
|
|
|
|
|
<methodname>messageConverters</methodname> bean property. The default
|
|
|
|
|
converter instances registered with the template are
|
|
|
|
|
<classname>ByteArrayHttpMessageConverter</classname>,
|
|
|
|
|
<classname>StringHttpMessageConverter</classname>,
|
|
|
|
|
<classname>FormHttpMessageConverter</classname> and
|
|
|
|
|
<classname>SourceHttpMessageConverter</classname>. You can override
|
|
|
|
|
these defaults using the <methodname>messageConverters</methodname> bean
|
|
|
|
|
property as would be required if using the
|
|
|
|
|
<classname>MarshallingHttpMessageConverter</classname>.</para>
|
|
|
|
|
|
|
|
|
|
<para>Each method takes URI template arguments in two forms, either as a
|
|
|
|
|
<literal>String</literal> variable length argument or a
|
|
|
|
|
<literal>Map<String,String></literal>. For example, </para>
|
|
|
|
|
|
|
|
|
|
<programlisting language="java">String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}",
|
|
|
|
|
String.class,"42", "21");
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
|
|
<para>using variable length arguments and </para>
|
|
|
|
|
|
|
|
|
|
<programlisting language="java">Map<String, String> vars = Collections.singletonMap("hotel", 42);
|
|
|
|
|
String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
|
|
<para>using a <literal>Map<String,String></literal>. </para>
|
|
|
|
|
|
|
|
|
|
<para>To create an instance of <classname>RestTemplate</classname> you
|
|
|
|
|
can simply call the default constructor. This will use standard Java
|
|
|
|
|
classes from the <literal>java.net</literal> package as the underlying
|
|
|
|
|
implementation to create HTTP requests. This can be overridden by
|
|
|
|
|
specifying an implementation of
|
|
|
|
|
<interfacename>ClientHttpRequestFactory</interfacename>. Spring provides
|
|
|
|
|
the implementation
|
|
|
|
|
<classname>CommonsClientHttpRequestFactory</classname> that uses the
|
|
|
|
|
Jakarta Commons <classname>HttpClient</classname> to create requests.
|
|
|
|
|
<classname>CommonsClientHttpRequestFactory</classname> is configured
|
|
|
|
|
using an instance of
|
|
|
|
|
<classname>org.apache.commons.httpclient.HttpClient</classname> which
|
|
|
|
|
can in turn be configured with credentials information or connection
|
|
|
|
|
pooling functionality.</para>
|
|
|
|
|
|
|
|
|
|
<para>The previous example using Jakarta Commons
|
|
|
|
|
<classname>HttpClient</classname> directly rewritten to use the
|
|
|
|
|
<classname>RestTemplate</classname> is shown below </para>
|
|
|
|
|
|
|
|
|
|
<programlisting>uri = "http://example.com/hotels/{id}/bookings";
|
|
|
|
|
|
|
|
|
|
RestTemplate template = new RestTemplate();
|
|
|
|
|
|
|
|
|
|
Booking booking = // create booking object
|
|
|
|
|
|
|
|
|
|
URI location = template.postForLocation(uri, booking, String.class, "1");
|
|
|
|
|
</programlisting>
|
|
|
|
|
|
|
|
|
|
<para>The general callback interface is
|
|
|
|
|
<interfacename>RequestCallback</interfacename> and is called when the
|
|
|
|
|
execute method is invoked.</para>
|
|
|
|
|
|
|
|
|
|
<programlisting language="java">public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback,
|
|
|
|
|
ResponseExtractor<T> responseExtractor,
|
|
|
|
|
String... urlVariables)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// also has an overload with urlVariables as a Map<String, String>.</programlisting>
|
|
|
|
|
|
|
|
|
|
<para>The <interfacename>RequestCallback</interfacename> interface is
|
|
|
|
|
defined as </para>
|
|
|
|
|
|
|
|
|
|
<programlisting language="java">public interface RequestCallback {
|
|
|
|
|
void doWithRequest(ClientHttpRequest request) throws IOException;
|
|
|
|
|
}</programlisting>
|
|
|
|
|
|
|
|
|
|
<para>and allows you to manipulate the request headers and write to the
|
|
|
|
|
request body. When using the execute method you do not have to worry
|
|
|
|
|
about any resource management, the template will always close the
|
|
|
|
|
request and handle any errors. Refer to the API documentation for more
|
|
|
|
|
information on using the execute method and the meaning of its other
|
|
|
|
|
method arguments.</para>
|
2009-04-09 03:29:02 +08:00
|
|
|
</section>
|
|
|
|
|
|
2009-04-11 13:59:12 +08:00
|
|
|
<section id="rest-message-conversion">
|
2009-04-09 03:29:02 +08:00
|
|
|
<title>HTTP Message Conversion</title>
|
|
|
|
|
|
2009-04-14 14:59:22 +08:00
|
|
|
<para>Objects passed to and returned from the methods getForObject(),
|
|
|
|
|
postForLocation(), and put() and are converted to HTTP requests and from
|
|
|
|
|
HTTP responses by <interfacename>HttpMessageConverters</interfacename>.
|
|
|
|
|
The <interfacename>HttpMessageConverter</interfacename> interface is
|
|
|
|
|
show below to give you a better feel for its functionality</para>
|
|
|
|
|
|
|
|
|
|
<programlisting language="java">public interface HttpMessageConverter<T> {
|
|
|
|
|
|
|
|
|
|
// Indicate whether the given class is supported by this converter.
|
|
|
|
|
boolean supports(Class<? extends T> clazz);
|
|
|
|
|
|
|
|
|
|
// Return the list of MediaType objects supported by this converter.
|
|
|
|
|
List<MediaType> getSupportedMediaTypes();
|
|
|
|
|
|
|
|
|
|
// Read an object of the given type form the given input message, and returns it.
|
|
|
|
|
T read(Class<T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException;
|
|
|
|
|
|
|
|
|
|
// Write an given object to the given output message.
|
|
|
|
|
void write(T t, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException;
|
|
|
|
|
|
|
|
|
|
}</programlisting>
|
|
|
|
|
|
|
|
|
|
<para>Concrete implementations for the main media (mime) types are
|
|
|
|
|
provided in the framework and are registered by default with the
|
|
|
|
|
<classname>RestTemplate</classname> on the client-side and with
|
|
|
|
|
<classname>AnnotationMethodHandlerAdapter</classname> on the
|
|
|
|
|
server-side. </para>
|
|
|
|
|
|
|
|
|
|
<para>The implementations of
|
|
|
|
|
<classname>HttpMessageConverter</classname>s are described in the
|
|
|
|
|
following sections. For all converters a default media type is used but
|
|
|
|
|
can be overridden by setting the
|
|
|
|
|
<classname>supportedMediaTypes</classname> bean property</para>
|
2009-04-09 03:29:02 +08:00
|
|
|
|
2009-04-11 13:59:12 +08:00
|
|
|
<section id="rest-string-converter">
|
2009-04-09 03:29:02 +08:00
|
|
|
<title>StringHttpMessageConverter</title>
|
|
|
|
|
|
2009-04-14 14:59:22 +08:00
|
|
|
<para>A <interfacename>HttpMessageConverter</interfacename>
|
|
|
|
|
implementation that can read and write Strings from the HTTP request
|
|
|
|
|
and response. By default, this converter supports all text media types
|
|
|
|
|
(<literal>text/*</literal>), and writes with a
|
|
|
|
|
<literal>Content-Type</literal> of
|
|
|
|
|
<literal>text/plain</literal>.</para>
|
2009-04-09 03:29:02 +08:00
|
|
|
</section>
|
|
|
|
|
|
2009-04-11 13:59:12 +08:00
|
|
|
<section id="rest-form-converter">
|
2009-04-09 03:29:02 +08:00
|
|
|
<title>FormHttpMessageConverter</title>
|
|
|
|
|
|
2009-04-14 14:59:22 +08:00
|
|
|
<para>A <interfacename>HttpMessageConverter</interfacename>
|
|
|
|
|
implementation that can read and write form data from the HTTP request
|
|
|
|
|
and response. By default, this converter reads and writes the media
|
|
|
|
|
type (<literal>application/x-www-form-urlencoded</literal>). Form data
|
|
|
|
|
is read from and written into a <literal>MultiValueMap<String,
|
|
|
|
|
String></literal>. </para>
|
2009-04-09 03:29:02 +08:00
|
|
|
</section>
|
|
|
|
|
|
2009-04-11 13:59:12 +08:00
|
|
|
<section id="rest-byte-converter">
|
2009-04-09 03:29:02 +08:00
|
|
|
<title>ByteArrayMessageConverter</title>
|
|
|
|
|
|
2009-04-14 14:59:22 +08:00
|
|
|
<para>A <interfacename>HttpMessageConverter</interfacename>
|
|
|
|
|
implementation that can read and write byte arrays from the HTTP
|
|
|
|
|
request and response. By default, this converter supports all media
|
|
|
|
|
types (<literal>*/*</literal>), and writes with a
|
|
|
|
|
<literal>Content-Type</literal> of
|
|
|
|
|
<literal>application/octet-stream</literal>. This can be overridden by
|
|
|
|
|
setting the <property>supportedMediaTypes</property> property, and
|
|
|
|
|
overriding <literal>getContentType(byte[])</literal>. </para>
|
2009-04-09 03:29:02 +08:00
|
|
|
</section>
|
|
|
|
|
|
2009-04-14 06:11:15 +08:00
|
|
|
<section id="rest-marhsalling-converter">
|
2009-04-09 03:29:02 +08:00
|
|
|
<title>MarshallingHttpMessageConverter</title>
|
|
|
|
|
|
2009-04-14 14:59:22 +08:00
|
|
|
<para>A <interfacename>HttpMessageConverter</interfacename>
|
|
|
|
|
implementation that can read and write XML using Spring's
|
|
|
|
|
<interfacename>Marshaller</interfacename> and
|
|
|
|
|
<interfacename>Unmarshaller</interfacename> abstractions from the
|
|
|
|
|
<classname>org.springframework.oxm</classname> package. This converter
|
|
|
|
|
requires a <interfacename>Marshaller</interfacename> and
|
|
|
|
|
<interfacename>Unmarshaller</interfacename> before it can be used.
|
|
|
|
|
These can be injected via constructor or bean properties. By default
|
|
|
|
|
this converter supports (<literal>text/xml</literal>) and
|
|
|
|
|
(<literal>application/xml</literal>).</para>
|
2009-04-09 03:29:02 +08:00
|
|
|
</section>
|
|
|
|
|
|
2009-04-11 13:59:12 +08:00
|
|
|
<section id="rest-source-converter">
|
2009-04-09 03:29:02 +08:00
|
|
|
<title>SourceHttpMessageConverter</title>
|
|
|
|
|
|
2009-04-14 14:59:22 +08:00
|
|
|
<para>A <interfacename>HttpMessageConverter</interfacename>
|
|
|
|
|
implementation that can read and write
|
|
|
|
|
<classname>javax.xml.transform.Source</classname> from the HTTP
|
|
|
|
|
request and response. Only <classname>DOMSource</classname>,
|
|
|
|
|
<classname>SAXSource</classname>, and
|
|
|
|
|
<classname>StreamSource</classname> are supported. By default, this
|
|
|
|
|
converter supports (<literal>text/xml</literal>) and
|
|
|
|
|
(<literal>application/xml</literal>).</para>
|
2009-04-09 03:29:02 +08:00
|
|
|
</section>
|
|
|
|
|
</section>
|
|
|
|
|
</section>
|
|
|
|
|
</chapter>
|