You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
548 lines
24 KiB
548 lines
24 KiB
<?xml version="1.0" encoding="UTF-8"?> |
|
<chapter id="rest"> |
|
<title>REST support</title> |
|
|
|
<section id="rest-introduction"> |
|
<title>Introduction</title> |
|
|
|
<para>The goal of Spring's REST support is to make the development of |
|
'RESTful' Web services and applications easier. 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 |
|
<classname>JmsTemplate</classname>. Instead of dealing with a verbose |
|
lower level API such as Apache Commons HttpClient to create RESTful |
|
request, RestTemplate provides one liner methods that are purpose built |
|
for RESTful programming. 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 say not implenting |
|
JAX-RS, read Arjen Putsma's SpringSource TeamBlog <ulink |
|
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 |
|
@RequestMapping and @PathVariable annotations and return different views |
|
as determined by the request's Context-Type header.</para> |
|
|
|
<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 |
|
controller configuration</link> to undestand the general programming |
|
model.</para> |
|
</section> |
|
|
|
<section id="rest-creating-services"> |
|
<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 |
|
linkend="mvc-servlet">DispatcherServlet</link>.</para> |
|
|
|
<section id="rest-uritemplate"> |
|
<title>URI templates</title> |
|
|
|
<para>RESTful services use URIs to name resourses. To faciliate |
|
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 |
|
RFC</ulink> for URI Templates defines how a URI is parameterized. For |
|
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> |
|
|
|
<para>When processing an request the URI can be compared to an expected |
|
URI Template in order to extract a collection of variables.</para> |
|
|
|
<para>Spring uses the <classname>@RequestMapping</classname> annotation |
|
to define the URI Template for the request. |
|
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> |
|
will bind the userId method parameter to the String value 'fred'.</para> |
|
|
|
<section id="rest-path-variable"> |
|
<title>Mapping RESTful URLs with the @PathVariable annotation</title> |
|
|
|
<para>The <classname>@PathVariable</classname> method level annotation |
|
is used to indicate that a method parameter should be bound to the |
|
value of a URI template variable.</para> |
|
|
|
<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 |
|
request, the value of ownerId is set the 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 |
|
ownerId</literal>.</para> |
|
|
|
<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> |
|
|
|
<programlisting language="java">@RequestMapping("/owners/{ownerId}", method=RequestMethod.GET) |
|
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, |
|
so you may also use create a controlled method with the signature |
|
shown below</para> |
|
|
|
<programlisting language="java">@RequestMapping("/owners/{ownerId}", method=RequestMethod.GET) |
|
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> |
|
</section> |
|
|
|
<section id="rest-multiple-representations"> |
|
<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 |
|
list of view resolvers set using the property |
|
<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, |
|
<classname>ContentNegotiatingViewResolver</classname>'s property |
|
<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> |
|
|
|
<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> |
|
|
|
<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 |
|
the section 'Atom Views'.</para> |
|
|
|
<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 |
|
and the same resolution of request to views would occur.</para> |
|
|
|
<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 { |
|
|
|
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; |
|
} |
|
|
|
}</programlisting> |
|
|
|
<para></para> |
|
</section> |
|
|
|
<section id="rest-views"> |
|
<title>Views</title> |
|
|
|
<para>Several views were added to Spring 3 to help support creating |
|
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 |
|
representation using Spring's Objecct/XML mapping (OXM) |
|
functionality</para> |
|
</listitem> |
|
</itemizedlist> |
|
|
|
<para>Available separately is the <classname>JacksonJsonView</classname> |
|
included as part of the Spring JavaScript project.</para> |
|
|
|
<section id="rest-feedview"> |
|
<title>Feed Views</title> |
|
|
|
<para>Both <classname>AbstractAtomFeedView</classname> and |
|
<classname>AbstractRssFeedView</classname> inherit from the base class |
|
<classname>AbstractFeedView<T></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 located in the package org.springframework.web.servlet.view.feed. |
|
The <classname>AbstractAtomFeedView</classname> requires you to |
|
implement the <methodname>buildFeedEntries</methodname> method and |
|
optionally override the <methodname>buildFeedMetadata</methodname> |
|
method (the default implementation is empty), as shown below</para> |
|
|
|
<programlisting>public class SampleContentAtomView extends AbstractAtomFeedView { |
|
|
|
@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> |
|
|
|
<programlisting>public class SampleContentAtomView extends AbstractRssFeedView { |
|
|
|
@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> |
|
|
|
<para>For an example implementation of creating a Atom view please |
|
refer to Alef Arendsen's SpringSource TeamBlog <ulink |
|
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> |
|
|
|
<para>The MarhsallingView</para> |
|
</section> |
|
</section> |
|
|
|
<section id="rest-method-conversion"> |
|
<title>HTTP Method Conversion</title> |
|
|
|
<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 |
|
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 |
|
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"> |
|
<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> |
|
|
|
<programlisting language="xml"><form:form method="delete"> |
|
<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> |
|
</section> |
|
|
|
<section id="rest-etag"> |
|
<title>ETag support</title> |
|
|
|
<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. |
|
The only thing it saves is bandwith, as the rendered response is not |
|
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> |
|
</section> |
|
|
|
<section id="rest-exception"> |
|
<title>Exception Handling</title> |
|
|
|
<para>@ExceptionHandler</para> |
|
</section> |
|
</section> |
|
|
|
<section id="rest-client-access"> |
|
<title>Accessing RESTful services on the Client</title> |
|
|
|
<para>Spring provides a client-side API blah blah</para> |
|
|
|
<section id="rest-resttemplate"> |
|
<title>RestTemplate</title> |
|
|
|
<para>blah blah</para> |
|
</section> |
|
|
|
<section id="rest-message-conversion"> |
|
<title>HTTP Message Conversion</title> |
|
|
|
<para>blah blah</para> |
|
|
|
<section id="rest-string-converter"> |
|
<title>StringHttpMessageConverter</title> |
|
|
|
<para></para> |
|
|
|
<para></para> |
|
</section> |
|
|
|
<section id="rest-form-converter"> |
|
<title>FormHttpMessageConverter</title> |
|
|
|
<para></para> |
|
|
|
<para></para> |
|
</section> |
|
|
|
<section id="rest-byte-converter"> |
|
<title>ByteArrayMessageConverter</title> |
|
|
|
<para></para> |
|
|
|
<para></para> |
|
</section> |
|
|
|
<section id="rest-marhsalling-converter"> |
|
<title>MarshallingHttpMessageConverter</title> |
|
|
|
<para></para> |
|
|
|
<para></para> |
|
</section> |
|
|
|
<section id="rest-source-converter"> |
|
<title>SourceHttpMessageConverter</title> |
|
|
|
<para></para> |
|
</section> |
|
</section> |
|
</section> |
|
</chapter> |