Create a Simple RESTful Web Service Using Spring MVC

In my previous post, we created a simple web application using Spring Boot.  It was a great starting off point, but we need a web service that actually accomplishes something for our fictional clients, the Rayburn’s. They need to be able to create and update guest data for their seaside resort in the Florida Keys. In this demo, we will add a web service to our Spring Boot Application using REST to their web application that can accept HTTP requests to update the embedded H2 database that we created in the previous post.

Create a Data Transfer Object

Since we want to save data to the database, we need to add persistence using the Java Persistence API (JPA).  We need to create a class to create a guest in our system. It will be a Data Transfer Object (DTO) which can travel between different layers in a system, thanks to JPA.

First, create new package under src/main/java for the DTO called com.rayburn.house.dto as shown in Figure 1.

package com.rayburn.house.dto;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="Guests")
public class GuestDTO {
	
	@Id
	@GeneratedValue
	@Column(name = "GUEST_ID")
	private Long id;
	
	@Column(name = "FIRST_NAME")
	private String firstName;
	
	@Column(name = "LAST_NAME")
	private String lastName;
	
	@Column(name = "ADDRESS")
	private String address;
	
	@Column(name = "CITY")
	private String city;
	
	@Column(name = "STATE")
	private String state;
	
	@Column(name = "ZIP")
	private String zip;
	
	@Column(name = "EMAIL")
	private String email;
	
	@Column(name = "BUNGALOW_NUM")
	private int bungalowNum;

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	public String getState() {
		return state;
	}

	public void setState(String state) {
		this.state = state;
	}

	public String getZip() {
		return zip;
	}

	public void setZip(String zip) {
		this.zip = zip;
	}

	public int getBungalowNum() {
		return bungalowNum;
	}

	public void setBungalowNum(int bungalowNum) {
		this.bungalowNum = bungalowNum;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}
}

Figure 1.

In the above class, the @Entity annotation tells JPA that we need persistence on this entity. @Table tells JPA that we want to associate this object with a table in H2 named Guests. @Id annotation tells it this is our primary key and @GeneratedValue tells it to generate an id for us. @Column maps the attribute to a database column.

Create a JPA Repository

Next we need to add a corresponding Data Access Object (DAO) otherwise known in JPA as a Repository object. A DAO defines how the application can retrieve and manipulate data. Our clients want to be able to to find the data by the guest’s last name.

Start by making sure that JPA is defined as a dependency adding the following to Maven’s pom.xml file:

<dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Figure 2.

Then, create a new package called com.rayburn.house.repository. Next, create a JpaRepository interface in your new package for your Guest information called GuestJpaRepository as follows:

package com.rayburn.house.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.rayburn.house.dto.GuestDTO;

@Repository
public interface GuestJpaRepository extends JpaRepository<GuestDTO, Long> {
	
	GuestDTO findByLastName(String lastName);
}
Figure 3.

You do not need to write an implementation of the repository interface. Spring Data JPA creates an implementation for you at run time. The JpaRespository expects your method names and query data to match those in the Guest DTO.

Create a Rest Controller

Now we need a Spring MVC controller where we will define REST endpoints. The Http POST and PUT mapped methods will consume data in the form of JSON files. The methods in our controller will work with JPA to interact with the data and package it neatly as ResponseEntity objects for us.

Create a package called com.rayburn.house.rest. Create a @RestController class called GuestRegistrationRestController as shown:

package com.rayburn.house.rest;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.rayburn.house.dto.GuestDTO;
import com.rayburn.house.repository.GuestJpaRepository;

@RestController
@RequestMapping("/api/guest")
public class GuestRegistrationRestController {
	
	private GuestJpaRepository guestJpaRepository;
	
	@Autowired
	public void setGuestJpaRepository(GuestJpaRepository guestJpaRepository) {
		this.guestJpaRepository = guestJpaRepository;
	}
	
	@PostMapping(value = "/", consumes = MediaType.APPLICATION_JSON_VALUE)
	public ResponseEntity<GuestDTO> addNewGuest(@RequestBody final GuestDTO guest){
		guestJpaRepository.save(guest);
		return new ResponseEntity<GuestDTO>(guest, HttpStatus.CREATED);
	}
	
	
	@GetMapping("/{id}")
	public ResponseEntity<GuestDTO> getGuestById(@PathVariable("id") final long id) {
		Optional<GuestDTO> guest = guestJpaRepository.findById(id);
		return new ResponseEntity<GuestDTO>(guest.get(),HttpStatus.OK);
	}
	
	
	@PutMapping(value = "/{id}", consumes = MediaType.APPLICATION_JSON_VALUE)
	public ResponseEntity<GuestDTO> updateGuest(@PathVariable("id") final Long id,
             @RequestBody GuestDTO guest) {
		
		Optional<GuestDTO> currentGuest = guestJpaRepository.findById(id);
		
		currentGuest.get().setFirstName(guest.getFirstName());
		currentGuest.get().setLastName(guest.getLastName());
		currentGuest.get().setAddress(guest.getAddress());
		currentGuest.get().setCity(guest.getCity());
		currentGuest.get().setState(guest.getState());
		currentGuest.get().setZip(guest.getZip());
		currentGuest.get().setEmail(guest.getEmail());
		currentGuest.get().setBungalowNum(guest.getBungalowNum());
		
		guestJpaRepository.saveAndFlush(currentGuest.get());
		
		return new ResponseEntity<GuestDTO>(currentGuest.get(), HttpStatus.OK);
	}
        @GetMapping("/")
	public ResponseEntity<List<GuestDTO>> listAllGuests() {
		List<GuestDTO> guests = guestJpaRepository.findAll();
		return new ResponseEntity<List<GuestDTO>(guests, HttpStatus.OK);
	}
	
	@DeleteMapping("/{id}")
	public ResponseEntity<GuestDTO> deleteGuest(@PathVariable("id") final Long id) {
		Optional<GuestDTO> currentGuest = guestJpaRepository.findById(id);
		guestJpaRepository.delete(currentGuest.get());
		return new ResponseEntity<GuestDTO>(HttpStatus.NO_CONTENT);
		
	}
    }
Figure 4.

In the above example, you used the @RestController annotation to define this class as a REST service controller. This means that every method is expected to return a domain object instead of a view. You added a @ReqestMapping annotation to map the URI /api/guest to the class. An HTTP request on that URI will be attended to by our new controller. The @Autowired annotation tells Spring that the GuestJpaRepository dependency needs to be injected here. Then we have the methods that will perform the HTTP CRUD operations. These are annotated with @PostMapping, @GetMapping, and @PutMapping, and @DeleteMapping.

Now, start your web service so that we can send it requests. If you are using spring boot, start your web service by right clicking on the project GuestRegistrationSystem then choosing Run As -> Spring Boot App.

Test Your Web Service

To test the web service, we need to download a rest client to help us create requests. Our web service has no front end and only will accept HTTP requests. One free REST client tool called Postman. Download it here: https://www.getpostman.com/apps. Postman lets you create HTTP requests from it’s user interface, send it to your server, and view the response.

First, let’s send an HTTP POST to our server. Because we annotated the addNewGuest method with @PostMapping, it will get called when we perform a POST operation. Also, because we configured it to consume JSON files, we must construct a JSON file to package the guest data in. Learn about JSON here: https://www.json.org/. Open Postman. Make sure the dropdown box is on “POST”. Enter http://localhost:8080/api/guest/ in the URL box. Making sure you are on the “Body” tab, construct a JSON file with the guest’s data in it as shown in Figure 5, and click Send.

HTTP Post using PostMan
Figure 5.

As you can see, the JSON file needs to contain one element per database column as defined in the GuestDTO class, except for the ID attribute which is auto generated. Next, test retrieving your new guest from the service by sending an HTTP GET operation and concatenating a user id to the end of the URL. If you are successful, you will see the guest in the response pane with a generated Id, and a return status of 200. See Figure 6.

HTTP Post using PostMan
Figure 6.

Because the getGuestById method was annotated with @GetMapping(“/{id}”), the getGuestById method is called in the above example. Add two or three more guests to the system using the POST operation. Next we will test with a HTTP GET with no id on the end.. Because we annotated the listAllGuests method with @GetMapping(“/”), we can retrieve all of the guests from the server with a URL of http://localhost:8080/api/guest/ with a GET operation.

A guest can be updated or deleted with a PUT and DELETE HTTP operation, thanks to the mapping in our REST Controller.

Next, we will add error handling to our web application.

Leave a Reply

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