Build RESTful web services with the Spring 3 MVC HttpMessageConverter feature

Spring, the well-known framework for building Java™ Platform, Enterprise Edition (Java EE) applications, now supports Representational State Transfer (REST) in its Model-View-Controller (MVC) layer. It's important for RESTful web services to produce multiple representations based on the client requests. In this article, learn to produce multiple representations with HttpMessageConverter. Code samples show how to use RestTemplate with HttpMessageConverter to communicate with services. Also, learn how to use Spring APIs and annotations to build RESTful web services that produce popular representations such as ATOM Feed, XML, and JavaScript Object Notation (JSON).

Share:

Yi Ming Huang, Software Engineer, IBM  

Yi Ming Huang, a Software Engineer at the China Development Lab, works on Lotus Mashups. He has experience on portlet and widget-related web development. Yi is interested in REST, OSGi, and Spring technologies.



07 September 2010

Also available in Chinese Russian Japanese

Introduction

A companion article, "Build RESTful web services using Spring 3," (see Resources) introduced the "Spring way" to build RESTful web services. It also explained how to use ContentNegotiatingViewResolver to produce multiple representations, which is an important feature for RESTful web services. This article explains another way to produce multiple representations using HttpMessageConverter, and examples in the article show how to use RestTemplate with HttpMessageConverter to communicate with services.


REST support in Spring MVC

This section provides an overview of the major Spring features, or annotations, that support RESTful web services.

@Controller
Use the @Controller annotation to annotate the class that will be the controller in MVC and handle the HTTP request.
@RequestMapping
Use the @RequestMapping annotation to annotate the function that should handle certain HTTP methods, URIs, or HTTP headers. This annotation is the key to the Spring REST support. You change the method parameter to handle other HTTP methods.

For example:

@RequestMapping(method=RequestMethod.GET, value="/emps", 
headers="Accept=application/xml, application/json")
@PathVariable
A path variable in the URI could be injected as a parameter using the @PathVariable annotation.

For example:

@RequestMapping(method=RequestMethod.GET, value="/emp/{id}")
public ModelAndView getEmployee(@PathVariable String id) { … }
Other useful annotations
Use @RequestParam to inject a URL parameter into the method.

Use @RequestHeader to inject a certain HTTP header into the method.

Use @RequestBody to inject an HTTP request body into the method.

Use @ResponseBody to return the content or object as the HTTP response body.

Use HttpEntity<T> to inject into the method automatically if you provide it as a parameter.

Use ResponseEntity<T> to return the HTTP response with your custom status or headers.

For example:

public @ResponseBody Employee getEmployeeBy(@RequestParam("name") 
String name, @RequestHeader("Accept") String accept, @RequestBody String body) {…} 
public ResponseEntity<String> method(HttpEntity<String> entity) {…}

Refer to the Spring documentation (see Resources) for a complete list of the supported annotations or objects that can be injected into the method.

Multiple representation support

Representing the same resource with different MIME types is an important aspect of RESTful web services. Generally, you would use the same URI with a different "accept" HTTP header to fetch the resource with different representation. You can also use a different URI, or a URI with different request parameters.

"Build RESTful web services using Spring 3" (see Resources) introduced ContentNegotiatingViewResolver, which can pick a different view resolver to handle the same URI (with the difference of the accept header). Thus, ContentNegotiatingViewResolver can be used to produce multiple representations.

There's also another way to produce multiple representations—by combining the HttpMessageConverter and c@ResponseBody annotation. With this method you don't need to use the View technologies.


HttpMessageConverter

HTTP requests and responses are text based, meaning a browser and server communicate by exchanging raw texts. With Spring, however, methods in the controller class return pure 'String' type and domain models (or other Java built-in objects). How can Spring serialize/de-serialize the objects to raw texts? This is handled by HttpMessageConverter. Spring has bundled implementations that can meet your common needs. Table 1 shows some examples.

Table 1. HttpMessageConverter examples
With...You can...
StringHttpMessageConverter Read/write a string from request and response. By default, it supports the media type text/* and writes with a Content-Type of text/plain.
FormHttpMessageConverter Read/write form data from request and response. By default, it reads the media type application/x-www-form-urlencoded and writes data into MultiValueMap<String,String>.
MarshallingHttpMessageConverterRead/write XML data using Spring's marshaller/un-marshaller. It converts data of media type application/xml.
MappingJacksonHttpMessageConverter Read/write JSON data using Jackson's ObjectMapper. It converts data of media type application/json.
AtomFeedHttpMessageConverterRead/write ATOM feed using ROME's Feed API. It converts data of media type application/atom+xml.
RssChannelHttpMessageConverter Read/write RSS feed using ROME's feed API. It converts data of media type application/rss+xml.

Build RESTful web services

In this section, learn to build a simple RESTful web service that can produce multiple representations. Some of the resources used in the sample application were built in "Build RESTful web services using Spring 3" (see Resources). You can also download the sample code.

First, you must configure the HttpMessageConverter. To produce multiple representations, customize several HttpMessageConverter instances to convert the object to different media types. This section covers the JSON, ATOM, and XML media types.

JSON

Let's start with the simplest example. JSON is a lightweight data-interchange format that's easy for humans to read and write. Listing 1 shows the code that configures a JSON converter.

Listing 1. Configure HttpMessageConverter in rest-servlet.xml
<bean class="org.springframework.web.servlet.mvc.annotation
.AnnotationMethodHandlerAdapter">
   <property name="messageConverters">
       <list>
           <ref bean="jsonConverter" />
   <ref bean="marshallingConverter" />
   <ref bean="atomConverter" />
       </list>
   </property>
</bean>

<bean id="jsonConverter" 
            class="org.springframework.http.converter.json
.MappingJacksonHttpMessageConverter">
   <property name="supportedMediaTypes" value="application/json" />
</bean>

In the configuration, three converters are registered. MappingJacksonHttpMessageConverter is used to convert the object to JSON and vice versa. This built-in converter uses Jackson's ObjectMapper to map JSON to the JavaBean, so you must add the following Jackson JAR files to the classpath.

  • org.codehaus.jackson.jar
  • org.codehaus.jackson.mapper.jar

The next step is to write a method to handle the request that asks for JSON representation. Listing 2 shows the details.

Listing 2. Handle JSON requests defined in EmployeeController
@RequestMapping(method=RequestMethod.GET, value="/emp/{id}", 
		headers="Accept=application/json")
public @ResponseBody Employee getEmp(@PathVariable String id) {
Employee e = employeeDS.get(Long.parseLong(id));
return e;
}
	
@RequestMapping(method=RequestMethod.GET, value="/emps", 
		headers="Accept=application/json")
public @ResponseBody EmployeeListinggetAllEmp() {
List<Employee> employees = employeeDS.getAll();
EmployeeListinglist = new EmployeeList(employees);
return list;
}

The @ResponseBody annotation is used to make the return object (Employee or EmployeeList) the response body content, which will be mapped by MappingJacksonHttpMessageConverter to JSON.

By using HttpMessageConverter and @ResponseBody, you can implement multiple representations without including Spring's View technology -- an advantage over using ContentNegotiatingViewResolver.

Now you can use the CURL or REST Client Firefox plug-in to invoke the request. Remember to add an HTTP header: Accept=application/json. Listing 3 shows the desired response in a JSON format.

Listing 3. JSON result for getEmp() and getAllEmp()
Response for /rest/service/emp/1
{"id":1,"name":"Huang Yi Ming","email":"huangyim@cn.ibm.com"}

Response for /rest/service/emps
{"count":2,
"employees":[
{"id":1,"name":"Huang Yi Ming","email":"huangyim@cn.ibm.com"},
{"id":2,"name":"Wu Dong Fei","email":"wudongf@cn.ibm.com"}
]}

XML

Spring's built-in converter MarshallingHttpMessageConverter is used to map between an object and XML (OXM). The example uses JAXB 2 as the marshaller/un-marshaller for the converter. Listing 4 shows the configuration.

Listing 4. Configure MarshallingHttpMessageConverter
<bean id="marshallingConverter" 
class="org.springframework.http.converter.xml
		.MarshallingHttpMessageConverter">
<constructor-arg ref="jaxbMarshaller" />
    <property name="supportedMediaTypes" value="application/xml"/>
      </bean>

      <bean id="jaxbMarshaller" 
      class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
      
    <property name="classesToBeBound">
	  <list>
	    <value>dw.spring3.rest.bean.Employee</value>
	    <value>dw.spring3.rest.bean.EmployeeList</value>
	  </list>
    </property>
    
</bean>

It's important to understand that JAXB 2 does not have good support for mapping the java.util.List<T> to XML. Common practice is to add a wrapper class for the collection of objects. Refer to "Build RESTful web services using Spring 3" (see Resources) or download the source code for details of this JAXB annotated class.

What about the method in the controller that handles the request? Take a look back at the code in Listing 2. It's not surprising to find that you don't need to add any code here. You just need to add another supported media type in the Accept header, as follows.

headers=”Accept=application/json, application/xml”

The converter will map the object properly to the type you request (JSON or XML). Listing 5 shows the desired result when you request the application/xml representation.

Listing 5. XML result for getEmp() and getAllEmp()
Response for /rest/service/emp/1
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <employee>
   <email>huangyim@cn.ibm.com</email>
   <id>1</id>
   <name>Huang Yi Ming</name>
 </employee>
Response for /rest/service/emps
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <employees>
  <count>2</count>
    <employee>
      <email>huangyim@cn.ibm.com</email>
      <id>1</id>
      <name>Huang Yi Ming</name>
    </employee>
    <employee>
      <email>wudongf@cn.ibm.com</email>
      <id>2</id><name>Wu Dong Fei</name>
    </employee>
 </employees>

ATOM feed

An ATOM feed is another popular format for exchanging data in RESTful web services. An Atom feed document is a representation of an Atom feed, including metadata about the feed, and some or all of the entries associated with it. Its root is the atom:feed element. There is also an ATOM Publish Protocol (APP) to define the exchange format and behaviors. (Defining the ATOM and APP formats is outside the scope of this article. See Resources for more information.)

The example uses AtomFeedHttpMessageConverter to convert the ATOM feed, which leverages the ROME ATOM API. Therefore, you must include the JAR file sun.syndication.jar in your classpath. Listing 6 shows the configuration of this converter.

Listing 6. Configure AtomFeedHttpMessageConverter
<bean id="atomConverter" 
class="org.springframework.http.converter.feed
		.AtomFeedHttpMessageConverter">
<property name="supportedMediaTypes" value="application/atom+xml" />
</bean>

Listing 7 shows the code that handles the ATOM request and the feed generation.

Listing 7. getEmpFeed() in EmployeeController & AtomUtil class
@RequestMapping(method=RequestMethod.GET, value="/emps", 
		headers="Accept=application/atom+xml")
public @ResponseBody Feed getEmpFeed() {
	List<Employee> employees = employeeDS.getAll();
	return AtomUtil.employeeFeed(employees, jaxb2Mashaller);
}

public static Feed employeeFeed(
	List<Employee> employees, Jaxb2Marshaller marshaller) {
Feed feed = new Feed();
feed.setFeedType("atom_1.0");
feed.setTitle("Employee Atom Feed");
		
List<Entry> entries = new ArrayList<Entry>();
for(Employee e : employees) {
	StreamResult result = new StreamResult(
	new ByteArrayOutputStream());
	marshaller.marshal(e, result);
	String xml = result.getOutputStream().toString();
			
	Entry entry = new Entry();
	entry.setId(Long.valueOf(e.getId()).toString());
	entry.setTitle(e.getName());
	Content content = new Content();
	content.setType(Content.XML);
	content.setValue(xml);
	
	List<Content> contents = new ArrayList<Content>();
	contents.add(content);
	entry.setContents(contents);
	entries.add(entry);
}
feed.setEntries(entries);
return feed;
}

In the code above, notice:

  • The getEmpFeed() method handles the same URI as getAllEmp(), but with a different Accept header.
  • With the employeeFeed() method you marshall the Employee object to XML and then add it to the <content> element of the feed entry.

Listing 8 shows the output when you request application/atom+xml representation for URI /rest/service/emps.

Listing 8. Output for /rest/service/emps when request application/atom+xml
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Employee Atom Feed</title>

 <entry>
    <title>Huang Yi Ming</title>
    <id>1</id>
  <content type="xml">
    <employee>
            <email>huangyim@cn.ibm.com</email>
            <id>1</id>
            <name>Huang Yi Ming</name>
    </employee>
  </content>
</entry>

  <entry>
    <title>Wu Dong Fei</title>
    <id>2</id>
  <content type="xml">
    <employee>
            <email>wudongf@cn.ibm.com</email>
            <id>2</id>
            <name>Wu Dong Fei</name>
     </employee>
   </content>
 </entry>
 
</feed>

Implement POST, PUT, and DELETE

The examples thus far have implemented several methods to handle the HTTP GET method. Listing 9 shows implementation of the POST, PUT, and DELETE methods.

Listing 9. POST, PUT, and DELETE methods in EmployeeController
@RequestMapping(method=RequestMethod.POST, value="/emp")
public @ResponseBody Employee addEmp(@RequestBody Employee e) {
employeeDS.add(e);
return e;
}
	
@RequestMapping(method=RequestMethod.PUT, value="/emp/{id}")
public @ResponseBody Employee updateEmp(
	@RequestBody Employee e, @PathVariable String id) {
employeeDS.update(e);
return e;
}
	
@RequestMapping(method=RequestMethod.DELETE, value="/emp/{id}")
public @ResponseBody void removeEmp(@PathVariable String id) {
employeeDS.remove(Long.parseLong(id));
}

The @RequestBody annotation is used in the addEmp() and updateEmp() methods. It takes the HTTP request body and tries to convert it to the object class using the registered HttpMessageConverter. In the next section, you'll use RestTemplate to communicate with these services.


Use RestTemplate to communicate with REST services

"Build RESTful web services using Spring 3" (see Resources) introduced how to use CURL and the REST client to test REST services. At the programming level, Jakarta Commons' HttpClient is typically used to do so (but that's outside the scope of this article). You can also use a Spring REST client called RestTemplate. It is conceptually similar to other template classes in Spring, such as JdbcTemplate and JmsTemplate.

RestTemplate also uses the HttpMessageConverter. You could pass the object class in the requests and let the converters handle the mappings.

Configuring the RestTemplate

Listing 10 shows the configuration of RestTemplate. It also uses the three converters introduced previously.

Listing 10. Configure RestTemplate
<bean id="restTemplate" 
class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
	<list>
	<ref bean="marshallingConverter" />
	<ref bean="atomConverter"  />
	<ref bean="jsonConverter" />
	</list>
</property>
</bean>

The examples in this article used only some of the methods that could simplify communication between servers. RestTemplate supports other methods, including:

  • exchange: executes certain HTTP methods with request body and gets the response.
  • getForObject: executes the HTTP GET method and gets the response as an object.
  • postForObject: executes the HTTP POST method with a certain request body.
  • put: executes the HTTP PUT method with a certain request body.
  • delete: executes the HTTP DELETE method for a certain URI.

Code samples

The following code samples help illustrate how to use RestTemplate. Refer to the RestTemplate API (see Resources) for a detailed explanation of the APIs used.

Listing 11 shows how to add headers to the request and then to invoke the request. Using the MarshallingHttpMessageConverter you could get the response and convert it to the typed class. You can use different media types to test other representations.

Listing 11. Request for XML representation
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
HttpEntity<String> entity = new HttpEntity<String>(headers);
ResponseEntity<EmployeeList> response = restTemplate.exchange(
"http://localhost:8080/rest/service/emps", 
HttpMethod.GET, entity, EmployeeList.class);
EmployeeListingemployees = response.getBody();
// handle the employees

Listing 12 shows how to POST a new employee to the server. The server-side service addEmp() could accept data with media types of application/xml and application/json.

Listing 12. POST a new employee
Employee newEmp = new Employee(99, "guest", "guest@ibm.com");
HttpEntity<Employee> entity = new HttpEntity<Employee>(newEmp);
ResponseEntity<Employee> response = restTemplate.postForEntity(
"http://localhost:8080/rest/service/emp", entity, Employee.class);
Employee e = response.getBody();
// handle the employee

Listing 13 shows how to PUT a modified employee to update the original one. It also shows the feature you can use as a place holder ({id}) in the request URI.

Listing 13. PUT to update an employee
Employee newEmp = new Employee(99, "guest99", "guest99@ibm.com");
HttpEntity<Employee> entity = new HttpEntity<Employee>(newEmp);
restTemplate.put(
	"http://localhost:8080/rest/service/emp/{id}", entity, "99");

Listing 14 shows how to DELETE an existing employee.

Listing 14. DELETE an existing employee
restTemplate.delete(
	"http://localhost:8080/rest/service/emp/{id}", "99");

Summary

In this article, you learned about the HttpMessageConverter that was introduced in Spring 3. It provides both client and server-side support for multiple representations. Using the source code provided, you can explore the differences between the implementation of HttpMessageConverter in this article and the implementation using ContentNegotiatingViewResolver in "Build RESTful web services using Spring 3."


Download

DescriptionNameSize
Article source codesrc_code.zip11KB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Web development on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development, SOA and web services
ArticleID=514033
ArticleTitle=Build RESTful web services with the Spring 3 MVC HttpMessageConverter feature
publish-date=09072010