Spring RESTful Webservice CRUD Example

Introduction

In this tutorial we will see how to build Spring RESTFul WebService CRUD example. Here we will use Spring version 4. We will create a controller that will manage CRUD operations such as Create, Read, Update and Delete using the correct HTTP request methods POST, GET, PUT and DELETE respectively.

You may also be interested to read Junit Testing Spring REST Services.

Prerequisites

Knowledge on Java, Spring, REST API, Maven

Example with Source Code

Here we will see how to create Spring RESTful webservices CRUD Example.

Creating Project

Here we create maven based web project in Eclipse. The name of the project is spring-rest.

Adding Dependencies

We configure the application using Java configuration, so we completely remove the web.xml , deployment descriptor, from the project.

We can use the maven-war-plugin to tell the compiler not to fail on a missing web.xml file, by setting the failOnMissingWebXml to false.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.roytuts</groupId>
	<artifactId>spring-rest</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-rest-junit Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<jdk.version>1.8</jdk.version>
		<junit.version>4.12</junit.version>
		<jackson.version>2.10.0</jackson.version>
		<servlet.version>3.1.0</servlet.version>
		<spring.version>4.3.8.RELEASE</spring.version>
	</properties>
	<dependencies>
		<!-- servlet -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>${servlet.version}</version>
			<scope>provided</scope>
		</dependency>
		<!-- spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<!-- jstl -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
			<scope>provided</scope>
		</dependency>
		<!-- jackson -->
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-core</artifactId>
			<version>${jackson.version}</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>${jackson.version}</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-annotations</artifactId>
			<version>${jackson.version}</version>
		</dependency>
	</dependencies>
	<build>
		<finalName>spring-rest-junit</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.3.2</version>
				<configuration>
					<source>${jdk.version}</source>
					<target>${jdk.version}</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.4</version>
				<configuration>
					<warSourceDirectory>src/main/webapp</warSourceDirectory>
					<warName>spring-rest-junit</warName>
					<failOnMissingWebXml>false</failOnMissingWebXml>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Creating Rest Controller

Let us start by implementing our Rest API. This controller exposes similar kind of the following Rest APIs:

HTTP GET request to /product/1 returns the user with id 1

HTTP POST request to /product with a product object in JSON creates a new product

HTTP PUT request to /product with a product object in JSON updates the product

HTTP DELETE request to /product/5 deletes the product with id 5

package roytuts.rest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;
import roytuts.model.Product;
import roytuts.sevice.ProductService;
@RestController
public class SpringRestController {
	@Autowired
	private ProductService productService;
	@RequestMapping(value = "/product/{id}", method = RequestMethod.GET)
	public ResponseEntity<Product> getProductByid(@PathVariable("id") int id) {
		Product product = productService.findProductById(id);
		if (product == null) {
			return new ResponseEntity<Product>(HttpStatus.NOT_FOUND);
		}
		return new ResponseEntity<Product>(product, HttpStatus.OK);
	}
	@RequestMapping(value = "/product", method = RequestMethod.POST)
	public ResponseEntity<Void> saveProduct(@RequestBody Product product, UriComponentsBuilder ucBuilder) {
		if (product == null || product.getName() == null || "".equals(product.getName())) {
			throw new RuntimeException("Product Name and Price are required fields");
		}
		if (productService.isProductAvailable(product)) {
			System.out.println("A Product with name " + product.getName() + " already exist");
			return new ResponseEntity<Void>(HttpStatus.CONFLICT);
		}
		productService.saveProduct(product);
		HttpHeaders headers = new HttpHeaders();
		headers.setLocation(ucBuilder.path("/product/{id}").buildAndExpand(product.getId()).toUri());
		return new ResponseEntity<Void>(headers, HttpStatus.CREATED);
	}
	@RequestMapping(value = "/product", method = RequestMethod.PUT)
	public ResponseEntity<Void> updateProduct(@RequestBody Product product) {
		if (product == null || product.getName() == null || "".equals(product.getName()) || product.getId() <= 0) {
			throw new RuntimeException("Product Name, Id, Price are required fields");
		}
		Product currentProduct = productService.findProductById(product.getId());
		if (currentProduct == null) {
			return new ResponseEntity<Void>(HttpStatus.NOT_FOUND);
		}
		productService.updateProduct(product);
		return new ResponseEntity<Void>(HttpStatus.OK);
	}
	@RequestMapping(value = "/product/{id}", method = RequestMethod.DELETE)
	public ResponseEntity<Void> deleteProductByid(@PathVariable("id") int id) {
		Product currentProduct = productService.findProductById(id);
		if (currentProduct == null) {
			return new ResponseEntity<Void>(HttpStatus.NOT_FOUND);
		}
		productService.deleteProductById(id);
		return new ResponseEntity<Void>(HttpStatus.OK);
	}
}

@RestController is a convenience annotation that does nothing more than adding the @Controller and @ResponseBody annotations.

 @RequestMapping annotation is used to map URLs such as /product onto an entire class or a particular handler method.

A class-level annotation like /product maps a specific request path onto a controller, with additional method-level annotations further narrowing the mapping for a specific HTTP request method like “GET”, “PUT”, “POST” or “DELETE” etc.

RequestMethod is an enumeration of possible HTTP request methods like GET, PUT, POST, DELETE, etc.

@PathVariable annotation indicates that a method parameter should be bound to a URI template variable. To process the @PathVariable annotation, Spring MVC needs to find the matching URI template variable by name. You can specify it in the annotation or, if the URI template variable name matches the method argument name, you can omit that detail.

@RequestBody annotation indicates a method parameter should be bound to the body of the HTTP web request. Behind the scene, a HttpMessageConverter is responsible for converting from the HTTP request message to an object and converting from an object to the HTTP response body.

ResponseEntity extends from HttpEntity which allows you to add a HttpStatus code directly to the response. The ResponseEntity represents the entire HTTP response. You can add header information, status codes and add content to the body.

Servlet Descriptor Initializer

Registers and configures the DispatcherServlet with the location of your Spring configuration file.

package roytuts.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class MvcWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
	@Override
	protected Class<?>[] getRootConfigClasses() {
		return new Class[] { WebMvcConfig.class };
	}
	@Override
	protected Class<?>[] getServletConfigClasses() {
		return null;
	}
	@Override
	protected String[] getServletMappings() {
		return new String[] { "/" };
	}
}

Spring MVC Configuration

Next, we need to configure Spring 4. We used Java Configuration. We will briefly explain the different configurations.

package roytuts.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "roytuts")
public class WebMvcConfig {
}

@EnableWebMvc imports the Spring MVC configuration from WebMvcConfigurationSupport if it is used in conjunction with @Configuration.

@ComponentScan configures component scanning directives for use with @Configuration. You need to register which packages it can scan to find your Spring components.

Creating Service Class

Here we have created below Spring service class to process data. We have not used any persistence repository for our example. So we are using some dummy data to fulfill our requests.

package roytuts.sevice;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.stereotype.Service;
import roytuts.model.Product;
@Service
public class ProductService {
	private static AtomicInteger counter = new AtomicInteger();
	private static List<Product> products;
	static {
		products = populateProducts();
	}
	public Product findProductById(int id) {
		for (Product product : products) {
			if (id == product.getId()) {
				return product;
			}
		}
		return null;
	}
	public void saveProduct(Product product) {
		product.setId(counter.incrementAndGet());
		products.add(product);
	}
	public void updateProduct(Product product) {
		int index = products.indexOf(product);
		products.set(index, product);
	}
	public void deleteProductById(int id) {
		Iterator<Product> it = products.iterator();
		while (it.hasNext()) {
			Product product = it.next();
			if (id == product.getId()) {
				it.remove();
			}
		}
	}
	public boolean isProductAvailable(Product product) {
		return findProductById(product.getId()) != null;
	}
	private static List<Product> populateProducts() {
		List<Product> products = new ArrayList<Product>();
		products.add(new Product(counter.incrementAndGet(), "Mobile", 25498.00));
		products.add(new Product(counter.incrementAndGet(), "Desktop", 32658.00));
		products.add(new Product(counter.incrementAndGet(), "Laptop", 52147.00));
		products.add(new Product(counter.incrementAndGet(), "Tab", 18254.00));
		return products;
	}
}

Creating Model Class

Below is the model class that will serve purpose for both repository as well as for VO or DTO object. In real world applications, the model class and VO or DTO classes should be created to fulfill the different purposes.

package roytuts.model;
public class Product {
	private int id;
	private String name;
	private double price;
	public Product() {
	}
	public Product(int id, String name, double price) {
		this.id = id;
		this.name = name;
		this.price = price;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public double getPrice() {
		return price;
	}
	public void setPrice(double price) {
		this.price = price;
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (int) (id ^ (id >>> 32));
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Product other = (Product) obj;
		if (id != other.id)
			return false;
		return true;
	}
}

Testing the Application

Here are the request and response structures of the above REST service end points.

POST

URL – http://localhost:8085/spring-rest/product/5

ContentType – Application/json

request – { “name”: “Mobile”, “price”: 25498.52 }

response – 201 (Created)

GET

URL – http://localhost:8085/spring-rest/product/5

response – { “id”: 5, “name”: “Mobile”, “price”: 25498.52 }

PUT

URL – http://localhost:8085/spring-rest/product/

request – { “id”: 5, “name”: “Tablet”, “price”: 25498.52 }

response – 200 (OK)

DELETE

URL – http://localhost:8085/spring-rest/product/5

response – 200 (OK)

Source Code

download source code

Thanks for reading.

Leave a Reply

Your email address will not be published. Required fields are marked *