REST support
Introduction 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 RestTemplate. RestTemplate follows in the footsteps of other template classes in Spring such as JdbcTemplate and JmsTemplate. 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 for not implementing JAX-RS, read Arjen Poutsma's SpringSource TeamBlog entry.) 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. 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 MVC framework, you may want to read through the reference documentation on annotation-based controller configuration to understand the general programming model.
Creating RESTful services 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 DispatcherServlet.
URI Templates RESTful services use URIs to name resources. To facilitate accessing the information contained in a URI, its structure follows conventions so that it can easily be described in a parameterized form. The proposed RFC for URI Templates defines how an URI is parameterized. For example, the URI Template http://www.example.com/users/{userid} contains the variable 'userid'. If we assign the variable the value "fred", then 'expanding' the URI Template gives. http://www.example.com/users/fred When processing a request the URI can be compared to an expected URI Template in order to extract a collection of variables. Spring uses the @RequestMapping method annotation to define the URI Template for the request. The @PathVariable 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; @RequestMapping("/users/{userid}", method=RequestMethod.GET) public String getUser(@PathVariable String userId) { // implementation omitted... } The request http://www.example.com/users/fred will bind the userId method parameter to the String value 'fred'.
Mapping RESTful URLs with the @PathVariable annotation The @PathVariable method parameter annotation is used to indicate that a method parameter should be bound to the value of a URI template variable. The following code snippet shows the use of a single @PathVariable in a controller method: @RequestMapping("/owners/{ownerId}", method=RequestMethod.GET) public String findOwner(@PathVariable String ownerId, Model model) { Owner owner = ownerService.findOwner(ownerId); model.addAttribute("owner", owner); return "displayOwner"; } The URI Template "/owners/{ownerId}" specifies the variable name ownerId. When the controller handles this 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 String ownerId. 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 @RequestMapping("/owners/{ownerId}", method=RequestMethod.GET) public String findOwner(@PathVariable("ownerId") String ownerId, Model model) { // implementation omitted } The name of the method parameter does not matter in this case, so you may also use a controller method with the signature shown below @RequestMapping("/owners/{ownerId}", method=RequestMethod.GET) public String findOwner(@PathVariable("ownerId") String theOwner, Model model) { // implementation omitted } Multiple @PathVariable annotations can be used to bind to multiple URI Template variables as shown below: @RequestMapping("/owners/{ownerId}/pets/{petId}", method=RequestMethod.GET) public String findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { Owner owner = ownerService.findOwner(ownderId); Pet pet = owner.getPet(petId); model.addAttribute("pet", pet); return "displayPet"; } The following code snippet shows the use of path variables on a relative path @Controller @RequestMapping("/owners/{ownerId}/**") public class RelativePathUriTemplateController { @RequestMapping("/pets/{petId}") public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { // implementation omitted } } method parameters that are decorated with the @PathVariable annotation can be of any simple type such as int, long, Date... Spring automatically converts to the appropriate type and throws a TypeMismatchException if the type is not correct.
Mapping the request body with the @RequestBody annotation The @RequestBody method parameter annotation is used to indicate that a method parameter should be bound to the value of the HTTP request body. For example, @RequestMapping(value = "/something", method = RequestMethod.PUT) public void handle(@RequestBody String body, Writer writer) throws IOException { writer.write(body); } The conversion of the request body to the method argument is done using a HttpMessageConverter. HttpMessageConverter is responsible for converting for converting from the HTTP request message to an object and converting from an object to the HTTP response body. DispatcherServlet supports annotation based processing using the DefaultAnnotationHandlerMapping and AnnotationMethodHandlerAdapter. In Spring 3 the AnnotationMethodHandlerAdapter has been extended to support the @RequestBody and has several HttpMessageConverters registered by default, these are ByteArrayHttpMessageConverter - converts byte arrays StringHttpMessageConverter - converts strings FormHttpMessageConverter - converts form data to/from a MultiValueMap<String, String> SourceHttpMessageConverter - convert to/from a javax.xml.transform.Source; MarshallingHttpMessageConverter - convert to/from an object using the org.springframework.oxm package. More information on these converters can be found in the section Message Converters. The MarshallingHttpMessageConverter requires a Marshaller and Unmarshaller from the org.springframework.oxm package to be configured on an instance of AnnotationMethodHandlerAdapter in the application context. For example <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"/>
Returning multiple representations 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. 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 http://www.example.com/users/fred.pdf requests a PDF representation of the user fred while http://www.example.com/users/fred.xml requests an XML representation. The second strategy is for the client to use the same URI to locate the resource but set the Accept HTTP request header to list the media types that it understands. For example, a HTTP request for http://www.example.com/users/fred with an Accept header set to application/pdf requests a PDF representation of the user fred while http://www.example.com/users/fred with an Accept header set to text/xml requests an XML representation. This strategy is known as content negotiation. 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 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 For this reason it is common to see the use of a distinct URI for each representation. To support multiple representations of a resource Spring provides the ContentNegotiatingViewResolver to resolve a view based on the file extension or Accept header of the HTTP request. ContentNegotiatingViewResolver does not perform the view resolution itself, but instead delegates to a list of view resolvers set using the bean property ViewResolvers. The ContentNegotiatingViewResolver selects an appropriate View to handle the request by comparing the request media type(s) with the media type (a.k.a. Content-Type) supported by the View associated with each of its ViewResolvers. The first View in the list that has a compatible Content-Type is used to return the representation to the client. The Accept header may include wild cards, for example 'text/*', in which case a View whose Context-Type was 'text/xml' is a compatible match. To support the resolution of a view based on a file extension, ContentNegotiatingViewResolver's bean property MediaTypes 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 ContentNegotiatingViewResolver.. Here is an example configuration of a ContentNegotiatingViewResolver <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"/> The InternalResourceViewResolver handles the translation of view names and JSP pages while the BeanNameViewResolver returns a view based on the name of a bean. (See "Resolving views - the ViewResolver interface" for more details on how Spring looks up and instantiates a view.) In this example, the content bean is a class that inherits from AbstractAtomFeedView which returns an Atom RSS feed. For more information on creating an Atom Feed representation see the section 'Atom Views'. 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 InternalResourceViewResolver 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 BeanNameViewResolver that maps to the SampleContentAtomView 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. If ContentNegotiatingViewResolver's list of ViewResolvers is not configured explicitly, then it will automatically use any ViewResolvers defined in the application context. The corresponding controller code that returns an Atom RSS feed for a URI of the form http://localhost/content.atom or http://localhost/content with an Accept header of application/atom+xml is shown below @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; } }
Views Several views were added in Spring 3 to help support creating RESTful services. They are: AbstractAtomFeedView - return an Atom feed AbstractRssFeedView - returns a RSS feed MarshallingView - returns an XML representation using Spring's Object to XML mapping (OXM) functionality Available separately is the JacksonJsonView included as part of the Spring JavaScript project.
Feed Views Both AbstractAtomFeedView and AbstractRssFeedView inherit from the base class AbstractFeedView and are used to provide Atom and RSS Feed views respectfully. They are based on java.net's ROME project and are located in the package org.springframework.web.servlet.view.feed. AbstractAtomFeedView requires you to implement the buildFeedEntries method and optionally override the buildFeedMetadata method (the default implementation is empty), as shown below 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 } } Similar requirements apply for implementing AbstractRssFeedView, as shown below 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 } } The buildFeedItems and buildFeedEntires 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. For an example of creating a Atom view please refer to Alef Arendsen's SpringSource TeamBlog entry.
XML Marshalling View The MarhsallingView uses a XML Marshaller defined in the org.springframework.oxm package to render the response content as XML. The object to be marshalled can be set explicitly using MarhsallingView's modelKey bean property. Alternatively, the view will iterate over all model properties marhsall only those types that are supported by the Marshaller.
HTTP Method Conversion 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 HiddenHttpMethodFilter 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.
Supporting Spring form tags 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 <form:form method="delete"> <p class="submit"><input type="submit" value="Delete Pet"/></p> </form:form> This will actually perform an HTTP POST, with the 'real' DELETE method hidden behind a request parameter, to be picked up by the HiddenHttpMethodFilter. The corresponding @Controller method is shown below @RequestMapping(method = RequestMethod.DELETE) public String deletePet(@PathVariable int ownerId, @PathVariable int petId) { this.clinic.deletePet(petId); return "redirect:/owners/" + ownerId; }
ETag support An ETag (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 Last-Modified header. When a server returns a representation with an ETag header, client can use this header in subsequent GETs, in a If-None-Match header. If the content has not changed, the server will return 304: Not Modified. Support for ETags is provided by the servlet filter ShallowEtagHeaderFilter. Since it is a plain Servlet Filter, and thus can be used in combination any web framework. The ShallowEtagHeaderFilter 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 If-None-Match value. The filter notices this, renders the view again, and compares the two hashes. If they are equal, a 304 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. 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.
Exception Handling The @ExceptionHandling 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 @Controller public class SimpleController { // other controller method omitted @ExceptionHandler(IOException.class) public String handleIOException(IOException ex, HttpServletRequest request) { return ClassUtils.getShortName(ex.getClass()); } } will invoke the 'handlerIOException' method when a java.io.IOException is thrown. The @ExceptionHandler 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 @ExceptionHandler will be invoked. If the annotation value is not set then the exception types listed as method arguments are used. Much like standard controller methods annotated with a @RequestMapping annotation, the method arguments and return values of @ExceptionHandler methods are very flexible. For example, the HttpServletRequest can be access in Servlet environments and the PortletRequest in Portlet environments. The return type can be a String, which is interpreted as a view name or a ModelAndView object. Please refer to the API documentation for more details.
Accessing RESTful services on the Client The RestTemplate is the core class for client-side access to RESTful services. It is conceptually similar to other template classes in Spring, such as JdbcTemplate and JmsTemplate and other template classes found in other Spring portfolio projects. RestTemplate's behavior is customized by providing callback methods and configuring the HttpMessageConverter 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 MarshallingHttpMessageConverter that uses the Object-to-XML framework that is part of the org.springframework.oxm package. This gives you a wide range of choices of XML to Object mapping technologies to choose from. This section describes how to use the RestTemplate and its associated HttpMessageConverters.
RestTemplate Invoking RESTful services in Java is typically done using a helper class such as Jakarta Commons HttpClient. For common REST operations this approach is too low level as shown below. 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()); } } 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. Overview of RestTemplate methods HTTP Method RestTemplate Method DELETE delete(String url, String… urlVariables) GET getForObject(String url, Class<T> responseType, String… urlVariables) HEAD headForHeaders(String url, String… urlVariables) OPTIONS optionsForAllow(String url, String… urlVariables) POST postForLocation(String url, Object request, String… urlVariables) PUT put(String url, Object request, String…urlVariables)
The names of RestTemplate 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 getForObject will perform a GET, convert the HTTP response into an object type of your choice, and returns that object. The method postForLocation 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 RestClientException will be thrown. Objects passed to and returned from these methods are converted to and from HTTP messages by HttpMessageConverter instances. Converters for the main mime types are registered by default, but you can also write your own converter and register it via the messageConverters bean property. The default converter instances registered with the template are ByteArrayHttpMessageConverter, StringHttpMessageConverter, FormHttpMessageConverter and SourceHttpMessageConverter. You can override these defaults using the messageConverters bean property as would be required if using the MarshallingHttpMessageConverter. Each method takes URI template arguments in two forms, either as a String variable length argument or a Map<String,String>. For example, String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}", String.class,"42", "21"); using variable length arguments and Map<String, String> vars = Collections.singletonMap("hotel", 42); String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars); using a Map<String,String>. To create an instance of RestTemplate you can simply call the default constructor. This will use standard Java classes from the java.net package as the underlying implementation to create HTTP requests. This can be overridden by specifying an implementation of ClientHttpRequestFactory. Spring provides the implementation CommonsClientHttpRequestFactory that uses the Jakarta Commons HttpClient to create requests. CommonsClientHttpRequestFactory is configured using an instance of org.apache.commons.httpclient.HttpClient which can in turn be configured with credentials information or connection pooling functionality. The previous example using Jakarta Commons HttpClient directly rewritten to use the RestTemplate is shown below 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"); The general callback interface is RequestCallback and is called when the execute method is invoked. 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>. The RequestCallback interface is defined as public interface RequestCallback { void doWithRequest(ClientHttpRequest request) throws IOException; } 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.
HTTP Message Conversion Objects passed to and returned from the methods getForObject(), postForLocation(), and put() and are converted to HTTP requests and from HTTP responses by HttpMessageConverters. The HttpMessageConverter interface is show below to give you a better feel for its functionality 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; } Concrete implementations for the main media (mime) types are provided in the framework and are registered by default with the RestTemplate on the client-side and with AnnotationMethodHandlerAdapter on the server-side. The implementations of HttpMessageConverters are described in the following sections. For all converters a default media type is used but can be overridden by setting the supportedMediaTypes bean property
StringHttpMessageConverter A HttpMessageConverter implementation that can read and write Strings from the HTTP request and response. By default, this converter supports all text media types (text/*), and writes with a Content-Type of text/plain.
FormHttpMessageConverter A HttpMessageConverter implementation that can read and write form data from the HTTP request and response. By default, this converter reads and writes the media type (application/x-www-form-urlencoded). Form data is read from and written into a MultiValueMap<String, String>.
ByteArrayMessageConverter A HttpMessageConverter implementation that can read and write byte arrays from the HTTP request and response. By default, this converter supports all media types (*/*), and writes with a Content-Type of application/octet-stream. This can be overridden by setting the supportedMediaTypes property, and overriding getContentType(byte[]).
MarshallingHttpMessageConverter A HttpMessageConverter implementation that can read and write XML using Spring's Marshaller and Unmarshaller abstractions from the org.springframework.oxm package. This converter requires a Marshaller and Unmarshaller before it can be used. These can be injected via constructor or bean properties. By default this converter supports (text/xml) and (application/xml).
SourceHttpMessageConverter A HttpMessageConverter implementation that can read and write javax.xml.transform.Source from the HTTP request and response. Only DOMSource, SAXSource, and StreamSource are supported. By default, this converter supports (text/xml) and (application/xml).