1
0
1
10,963

REST Web-Services with Spring MVC

REST Web-Services with Spring MVC The 5 Steps Cookbook

Abstract

Spring REST-WS I outline 5 simple steps to implement REST Web-Services with Spring MVC.

The Way of REST Web-Services

REST is an architecture style that describes the set of principles, best practices, and guidelines that applications should follow to expose Web Services APIs over HTTP protocol. Following these principles and supporting middleware frameworks, applications are provided with the means to export distributed services to other client applications that access their managed resources. In inter-networking parlance, it is commonly said that applications use HTTP as an application protocol (, rather than a simple transport layer protocol).

REST is an acronym for REpresentational State Transfer, a term coined by Roy Fielding as part of this doctoral dissertation. Using timely social networking, Roy Fielding joined Tim Berners-Lee, the inventor of WWW, to become one of the lead authors of the specification for HTTP1.0, and later HTTP1.1, protocol.

The REST principles in a NutShell

Rest Web-services follow a small but powerful set of design principles:
  1. An URI scheme to address Resources - All applications resources are identified and accessible by their Unique Resource Identifier. The set of such identifiers is the URI schema or namespace of the application. Resources can be anything the application wants, but typically are domain relevant objects - entity types - managed and stored on the behalf of users or other applications. For example, an application for educational courses might have Student and Module as managed entities. The URI schema create for this application could include URI such as:
    EntityURI (prefix)
    Studenthttp://hostname[:port]/{context}/student
    Modulehttp://hostname[:port]/{context}/module
  2. Operations - REST web-services use the set of HTTP methods to defined operations supported on the managed resource. This is a rather limited set operations and includes, mostly: POST, PUT, GET, and DELETE. This HTTP methods are a well defined, and applications are recommend to follow their intended meaning. The combination of resource URI schema and the HTTP methods defines the set of service methods or operations exported by the application.
  3. Representation - Resources are abstracted as representation neutral. Client applications can request for particular representation of resources. For data exchange, formats like, XML, JSON, and HTML, are very common.
    Resource representations can also include hyperlinks to another resources, as a mechanism of service discovery. However, this technique is not often explored in practice.
  4. Stateless architecture - REST Web-Services promote a server architecture that is stateless. That is, servers are not required to maintain conversational state about clients. Once a REST request is completed, not information about the client needs to be keep (e.g. in a Session). This stateless aspect contributes to increase the scalability of the application architecture. It also means, that REST requests may have to include authentication details with every request.
  5. Error Handling - Http defines a standard ways by which errors during request processing errors can be reported. This are the Status code, whose meanings are well defined. Applications can map internal or request exceptions to status codes, rather than (re)invent custom approaches.

    Sometimes applications need to return more information to clients that simple error coders. For examples, when reporting binding errors in a AJAX form POST. This has to be done in a custom way.

The advantages of REST

  • REST uses HTTP as a full application layer protocol, unlike SOAP Web Services or HTTP based remote method invocation, which use HTTP as a simple transport layer protocol. Hence REST can take advantage of HTTP abstractions, mechanisms, components libraries, and infrastructure. Key mechanisms include: cache management, content negotiation, proxying.
  • Now a days most of the client applications are internet facing applications and will send requests to server and get responses. These client applications are widely used in mobile devices. It is very simple and easy to use REST APIs as it uses HTTP protocol and to build REST client application, it required HTTP client libraries.

Implementing RESTful Web-Services in Java

There are several frameworks and options to implement RESTfull web-services in Java. Two that we will focus here are:

  • JAX-RS
  • Spring MVC

JAX-RS is a standard JEE specification to defined REST web-service as server-side resources. The reference implementation is Jersey. For Spring Framework experienced developer, Spring MVC is probably a more familiar way to move forward. In this article, we will focus mostly on the Spring MVC based implementation. In additional to implement the server-side, Spring Framework also provides a convenient way of consuming REST APIs in client applications based on the class RestTemplate. Spring MVC is a powerful framework and it will take care of complete URL mapping, parsing and which controller or handler method to invoke and what parameters to pass to those methods, etc.

RESTful Web Services: The Spring MVC Way

Implementing RESTful Web Services with Spring MVC can be roughly be broken in 5 essential steps.

Step #1: Configuration of "web.xml" file

In the web application descriptor file, "/WEB-INF/web.xml" file, you will be configuring how and where to load the application layer context and Spring MVC web layer context files from and also the servlet URL.


<context-param> <!--Application Layer context file -->
	<param-name>contextConfigLocation</param-name>
	<param-value>/WEB-INF/app-config.xml</param-value>
</context-param>
<listener>
	<!-- Create root applicationContext -->	
	<listener-class>
	org.springframework.web.context.ContextLoaderListener
	</listener-class> 
</listener>
<servlet>
	<!-- Spring MVC Engine -->
	<servlet-name>studentSystem</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<!-- Web-Layer applicationContext -->
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/mvc-config.xml</param-value>
	</init-param>
</servlet>

What is Application Context? An applicationContext is a configuration file contains instructions on how and what to inject or wire the dependency on the depended object or a bean. You can find more details on an application context at:

http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/context/ApplicationContext.html

Web application architecture contains two layers and for each layer, there is a separate application context configuration file created to keep each layer separate.

Root and Web Application Context

  1. Application Layer: You will configure all Business Service classes, Repository classes and etc. To create application layer context, it uses the ServletListner to create application context file.
  2. Web Layer: You will configure all controller classes, view resolvers, and etc. To create web layer context, it uses the DispatcherServlet and when it creates the web layer context, it become child and application layer context will become parent context.

Step #2: Controller Java class

Base Controller annotation interface, representing a component that receives HttpServletRequest and HttpServletResponse like a HttpServlet but is able to participate in an MVC workflow. Comparable to the notion of a Struts Action. Any implementation of the Controller interface should be a reusable, thread-safe class, capable of handling multiple HTTP requests throughout the lifecycle of an application. To be able to configure Controller in an easy way. Controllers are usually JavaBeans.

Create a Controller Java class and add @Controller annotations to participate in Spring MVC workflow. Table below summaries some of the key annotations used in Spring controllers.


@Controller
public class StudentController {
	
	@Autowired
	private StudentDAO studentDAO;

	@RequestMapping(value="/student/{id}", method=RequestMethod.GET)
	public @ResponseBody Student getStudentDetails(@PathVariable("id") Integer studentId) {
		return studentDAO.getStudentDetails(studentId);
	}
	
	@RequestMapping(value="/student/", method=RequestMethod.POST)
	public void createStudent(@RequestBody Student student) {
			studentDAO.save(student);
	}
}

AnnotationLevelDescription
@ControllerTypeStereotype annotation to mark a web-layer component/bean.
@RequestMappingMethod, Type Specifies the URL pattern for which the handler method should be mapped The HTTP operation(s) can also be specified to restrict the mapping.
@ResponseBodyMethodFor the HTTP GET operation, you need to send the response back with expected data. HandlerAdptor will take care of converting the object to XML/JSON using HttpMessageConvertor. Same way if any input data need to convert and pass it as Object, you can use @RequestBody.
@RequestBodyParameterUsed in HTTP POST and PUT operations, to specify that the parameter value should be extracted from ther body of the request.
@PathVariableParameterUsed to extract a named path segment from the URL pattern.

Step #3: Spring MVC Application context file

As explained in Step #1, there is a MVC application context configuration file contains all Controller beans, and other Spring MVC framework beans to configure in MVC Application context.

File: /WEB-INF/mvc-config.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc     http://www.springframework.org/schema/mvc/spring-mvc.xsd">

<!-- It scans all @Controller class and creates bean instances -->
	<context:component-scan base-package="accounts.web"/>	

	<!--  It will setup all Spring MVC components --> 
	<mvc:annotation-driven/>
</beans>

Step #4: Application Layer in Application Context file

Application Layer context file is divided into two parts: Infrastructure and application modules beans configuration. Both configuration files are imported into one single application layer context configuration file (like: /WEB-INF/app-config.xml) Infrastructure Application context: The configuration file contains all infrastructure (like, DatabaseSource, SessionFactory, etc) (File: module/infrastructure-config.xml)


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"	
http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
		<property name="url" value="jdbc:mysql://localhost:3306/studentSystem"/>
		<property name="username" value="manjunp"/>
		<property name="password" value="manjunp"/>
	</bean>

	<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource"/>
		<property name="annotatedClasses">
			<list>
				<value>com.eklakshya.model.Student</value>
				<value>com.eklakshya.model.Module</value>
			</list>
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="current_session_context_class">thread</prop>
			<prop key="show_sql">true</prop>
			<prop key="hibernate.hbm2ddl.auto">create</prop>
			<prop key="hibernate.show_sql">true</prop>
			<prop key="hibernate.use_sql_comments">true</prop>
			<prop key="hibernate.format_sql">true</prop>
			</props>
		</property>
	</bean>
	
	<bean id="studentDAO" class="com.eklakshya.repository.StudentDAO">
		<property name="sessionFactory" ref="sessionFactory"/>
	</bean>
	
</beans>

Infrastructure and application module specific configuration files into one master application layer configuration file.


<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
<import resource="classpath:module/application-config.xml"/>
<import resource="classpath:module/infrastructure-config.xml"/>
</beans>

HTTP Status Code Support

Web Applications uses handful of HTTP Status Code to identify and display messages. REST applications use many additional codes to communicate with their client.

Some of HTTP Status codes include

Status CodeDescription
200After a successful GET where content is returned
201When new resource was created on POST or PUT. Location header should contain URI of new resource
204When the response is empty. e.g. after successful update with PUT or DELETE
404When requested resource was not found
405when HTTP method is not supported by resource
409When a conflict occurs while making changes. e.g. when POSTing unique data that already exists
500Internal server error

Annotation to represent the status code in controller


@RequestMapping(value="/student/", method=RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED) //201
public void createStudent(@RequestBody Student student)  {
	studentDAO.save(student);
}
	

When Student record has created on DB and for the update, view and delete, application requires a student_id. To get the student_id, we can write another REST API to get the student ID given Student Name and email ID (few unique attributes to find out the record). But it will be expensive to search and get the student record. Another best way to send the student ID back to client when you create a student record, server can put student ID information part of the HTTP Response header and send it back to client application.


@RequestMapping(value="/student/", method=RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED) //201
public void createStudentAndResponse(HttpServletRequest request,
	HttpServletResponse response, @RequestBody Student student)  {
	studentDAO.save(student);
	String uri = getURIForChildResource(request, student.getId());
	response.addHeader("Location", uri);
}
	
private String getURIForChildResource(HttpServletRequest request, 
		Object childIdentifier) {
	StringBuffer url = request.getRequestURL();
	UriTemplate template = new UriTemplate(url.append("/{childId}").toString());
	return template.expand(childIdentifier).toASCIIString();
}

The value of the "Location" attribute from HTTP Response header must need to be full URL. The full URL need to determine based on the HTTP Request. URL of created child resource usually a sub-path POST to http://<server name>/restProject/studentSystem/student The URL need to send back along with Location header is:

http://<server name>/restProject/studentSyatem/student/10

Can use Spring's UriTemplate for encoding where needed (See method getURIForChildResource() above).

Error Handling: ResponseStatus and Exceptions

While fetching information for the required resource is not available, in such case the server should return Status Code as 404 Not Found. If the same error code needs to use in several places, you can create a place-holder exception class and use it other places.


@ResponseStatus(HttpStatus.NOT_FOUND)  // 404
public class StudentNotFoundException extends RuntimeException {
	  //Constuctors
	  ...
}


@RequestMapping(value="/student/{id}", method=RequestMethod.GET)
public @ResponseBody Student getStudentDetails(@PathVariable("id")Integer studentId) {
	Student student = studentDAO.getStudentDetails(studentId);
	if (student == null) {
		throw new StudentNotFoundException();
	}
	return student;
}

Message Converters

REST Web Services supports multiple representations to exchange the data from REST Client to Server. It supports: JSON, XML, HTTP, etc. In order to support these formats or representation, it requires 3rd party libraries. Various implementations registered by default when using <mvc:annotation-driven/>

  • XML (using JAXP Source or JAXB2 mapped object*)
  • Feed data*, i.e. Atom/RSS
  • Form-based data
  • JSON*
  • Byte[], String, BufferedImage

Define HandlerAdapter explicitly to register other HttpMessageConverters Based on the 3rd party libraries availability, HttpMessageConverters will automatically setup for you. You do not need to write anything extra in your controller code. HandlerAdapter will take care of converting to object for incoming request and for outgoing response; it will convert back to best available format based on the client content negotiation. The HandlerAdapter will set the ContentType in the HTTP Response header. Content negotiation: HTTP Client will negotiate the representation in which data is required. Client can request multiple representation and server will take all requested representation and server will choose the best and first available message convertor and send the response.

Understanding of HTTP Request and HTTP Response Headers

HTTP Request

A HTTP request message from a client to a server includes, within the first line of that message, the method to be applied to the resource, the identifier of the resource, and the protocol version in use. For example:

GET /restProject/studentSystem/student/1 HTTP/1.1

HTTP Response

Status Line
The first line of a Response message is the Status-Line, consisting of the protocol version followed by a numeric status code and its associated textual phrase - e.g.: HTTP/1.1 200 OK
Content-Type
The Content-Type entity-header field indicates the media type of the entity-body to the recipient. e.g: "application/xml" or "application/json".
Transfer-Encoding
The Transfer-Encoding general-header field indicates what (if any) type of transformation has been applied to the message body in order to safely transfer it between the sender and the recipient. This differs from the content-coding in that the transfer-coding is a property of the message, not of the entity.
Server
The Server response-header field contains information about the software used by the origin server to handle the request The field can contain multiple product tokens and comments identifying the server and any significant sub-products. The product tokens are listed in order of their significance for identifying the application.
Content-Length
The Content-Length entity-header field indicates the size of the entity-body, in decimal number of OCTETs, sent to the recipient.
Entity-body
The entity-body (if any) sent with an HTTP request or response is in a format and encoding defined by the entity-header fields. An entity-body is only present in a message when a message-body is present. The entity-body is obtained from the message-body by decoding any Transfer-Encoding that might have been applied to ensure safe and proper transfer of the message. In case of RESTful HTTP Request and Response, XML and JSON are typically used data representations.

Putting HTTP Request and Response together

GET Request

The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI. If the Request-URI refers to a data-producing process, it is the produced data which shall be returned as the entity in the response.


GET /restProject/studentSystem/student/1 HTTP/1.1
Accept: application/json
Content-Length: 0
User-Agent: Java/1.6.0_18
Host: localhost:8080
Connection: keep-alive


HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/json
Transfer-Encoding: chunked
Date: Mon, 01 Jul 2013 11:12:33 GMT
{ "id" : 1, "name" : "Manjunath", "address": "BLR", ...}


@RequestMapping(value="/student/{id}", method=RequestMethod.GET)
public @ResponseBody Student getStudentDetails(@PathVariable("id")Integer studentId) {
	Student student = studentDAO.getStudentDetails(studentId);
	if (student == null) {
		throw new StudentNotFoundException();
	}
	return student;
}

POST Request

The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line.


POST /restProject/studentSystem/student/ HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 355
User-Agent: Java/1.6.0_18
Host: localhost:8080
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive

{ "id" : null, "name" : "Manjunatha H", ... }


HTTP/1.1 201 Created
Server: Apache-Coyote/1.1
Location: http://localhost:8080/restProject/studentSystem/student/7
Content-Length: 0
Date: Mon, 01 Jul 2013 11:12:33 GMT


@RequestMapping(value="/student/", method=RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public void createStudentAndResponse(HttpServletRequest request,
		HttpServletResponse response, @RequestBody Student student)  {
	studentDAO.save(student);
	String uri = getURIForChildResource(request, student.getId());
	response.addHeader("Location", uri);
}

PUT Request

The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server.


PUT /restProject/studentSystem/student/ HTTP/1.1
Content-Type: application/json;charset=UTF-8
Content-Length: 328
User-Agent: Java/1.6.0_18
Host: localhost:8080
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive

{ "id": 1, "name": "Manjunatha Hiremath", "address": ... }


HTTP/1.1 204 No Content
Server: Apache-Coyote/1.1
Date: Mon, 01 Jul 2013 11:39:43 GMT


@RequestMapping(value="/student/", method=RequestMethod.PUT)
@ResponseStatus( HttpStatus.OK )
public void updateStudent(@RequestBody Student student) {
	studentDAO.update(student);
}

DELETE Request

The DELETE method requests that the origin server delete the resource identified by the Request-URI.


DELETE /restProject/studentSystem/student/7 HTTP/1.1
Content-Length: 0
User-Agent: Java/1.6.0_18
Host: localhost:8080
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive


HTTP/1.1 204 No Content
Server: Apache-Coyote/1.1
Date: Mon, 01 Jul 2013 11:39:43 GMT


@RequestMapping(value="/student/{id}", method=RequestMethod.DELETE)
@ResponseStatus( HttpStatus.NO_CONTENT )
public void deleteStudent(@PathVariable("id")Integer studentId) {
	Student student = studentDAO.getStudentDetails(studentId);
	if (student == null) {
		throw new StudentNotFoundException();
	}
	try {
		studentDAO.delete(student);
	} catch (Exception e) {
		// Can't delete the Student Record throw an exception
		throw new OperationFailedException();
	}
}

Step #5: Consuming REST APIs using REST Template

In Spring Framework, the key abstraction to consume REST web-services is class RestTemplate. It provides a simple, flexible, and complete API, in which the HTTP methods are mapped in a more-or-less one-to-one correspondence to methods in the API. A best-practice when implementing REST web-service is to always perform end2end tests of the implemented service endpoints. RestTemplate provides a convenient and effective way to do that.

Table below summaries the key methods in the API of RestTemplate:

HTTP MethodRestTemplate Method
GETgetForObject(String url, Class<?> type, params...)
POSTpostForLocation(String url, Object obj, params...)
PUTputForObject(String url, Object obj, params...)
DELETEdelete(String url, params...)

Below, we show the code sample to perform an end2end test of a REST-WS using the RestTemplate:


public class RestClientTest {
	
	private static final String BASE_URL = "http://localhost:8080/restProject/studentSystem";
	
	RestTemplate< restTemplate = new RestTemplate();

	String url = BASE_URL + "/student/1";
	String url_put = BASE_URL + "/student/";
	String url_delete = BASE_URL + "/student/7";
	
	@Test
	public void getStudentTest() {
		Student student = restTemplate.getForObject(url, Student.class );
		assertNotNull(student);
		assertEquals("Mike", student.getName());
		assertEquals("mike@jpalace.org", student.getEmail());
	}

	@Test
	public void updateStudentTest() {
		Student student = restTemplate.getForObject(url, Student.class );
		student.setEmail("mike@spring.io");
		restTemplate.put(url_put, student);
		Student student = restTemplate.getForObject(url, Student.class );
		assertNotNull(student);
		assertEquals("mike@spring.io", student.getEmail());
	}

	@Test
	public void deleteStudentTest() {
		restTemplate.delete(url_delete);
		try {
			restTemplate.getForObject(url, Student.class );
			fail();
		} catch (RuntimeException e) {
		}
	}

	@Test
	public void createStudentTest() {
		Student student = new Student();
		student.setAddress("SpringPeople");
		student.setEmail("manjunath@gmail.com");
		student.setName("Manjunath");		
		URI uri = restTemplate.postForLocation(url, student);
		assertNotNull(uri);
	}
}

Summary

REST is an architectural style that can be applied to HTTP-based applications. Useful for supporting diverse clients and building highly scalable systems. If you are already familiar to Spring Framework programming model, you can build REST API using Spring MVC as core technology. In Spring framework, the class RestTemplate provides an effective way to implement clients and write end2ends test that access/consume the RESTful Web-Services.


No Comments

Post First Comment

Login (or Register)
Contribute Feedback