spring-framework/spring-framework-reference/src/rest.xml

281 lines
13 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<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.</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
<classname>JmsTemplate</classname>. Instead of dealing with a verbose
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
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
<classname>@RequestMapping</classname> and
<classname>@PathVariable</classname> annotations and return different
views as determined by the request's <literal>Context-Type</literal>
header.</para>
<para>In this chapter we describe all the features of Spring's REST
support. It is divided into two main 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 understand the general programming model.</para>
</section>
<section id="rest-views">
<title>Views</title>
<para>Several views were added in Spring 3 to help support creating
RESTful services. They are:</para>
<itemizedlist>
<listitem>
<para><classname>AbstractAtomFeedView</classname> - returns 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 Object to XML mapping (OXM)
functionality</para>
</listitem>
</itemizedlist>
<note>
<para>Available separately is the
<classname>JacksonJsonView</classname> included as part of the Spring
JavaScript project.</para>
</note>
<section id="rest-feedview">
<title>Feed Views</title>
<para>Both <classname>AbstractAtomFeedView</classname> and
<classname>AbstractRssFeedView</classname> inherit from the base class
<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
implement the <methodname>buildFeedEntries</methodname> method and
optionally override the <methodname>buildFeedMetadata</methodname>
method (the default implementation is empty), as shown below</para>
<programlisting language="java">public class SampleContentAtomView extends AbstractAtomFeedView {
@Override
protected void buildFeedMetadata(Map&lt;String, Object&gt; model, Feed feed,
HttpServletRequest request) {
// implementation omitted
}
@Override
protected List&lt;Entry&gt; buildFeedEntries(Map&lt;String, Object&gt; model,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
// implementation omitted
}
}</programlisting>
<para>Similar requirements apply for implementing
<classname>AbstractRssFeedView</classname>, as shown below</para>
<programlisting language="java">public class SampleContentAtomView extends AbstractRssFeedView {
@Override
protected void buildFeedMetadata(Map&lt;String, Object&gt; model, Channel feed,
HttpServletRequest request) {
// implementation omitted
}
@Override
protected List&lt;Item&gt; buildFeedItems(Map&lt;String, Object&gt; 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 is 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 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 <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>. For
more information on the functionality in the
<classname>org.springframework.oxm</classname> package refer to the
chapter <link linkend="oxm">Marshalling XML using O/X
Mappers</link>.</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 methods: 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">&lt;form:form method="delete"&gt;
&lt;p class="submit"&gt;&lt;input type="submit" value="Delete Pet"/&gt;&lt;/p&gt;
&lt;/form:form&gt;</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, the client can use this header in
subsequent GETs, in an <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 with any web
framework. The <classname>ShallowEtagHeaderFilter</classname> filter
creates so-called shallow ETags (as opposed to 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 an MD5
hash over that, and returns that as an ETag header in the response. The
next time a client sends a request for the same resource, it uses 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 bandwidth, 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>The <classname>@ExceptionHandler</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 accessed 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>
</section>
</chapter>