HTTP Request Methods
The most commonly-used HTTP request methods are GET
, POST
, PUT
, DELETE
, PATCH
. They represent the read
, create
, create/replace
, delete
and update
operations, respectively.
An HTTP method is safe if it is an read-only opeartion. For example, the GET
method is safe because it never alters the state of the server.
An HTTP method is idempotent if an HTTP method can be made many times without any side-effects. For instance, the PUT
method is idempotent because it replaces the same resource repeatedly.
Below is a table showing the saftey and idempotency of some HTTP method.
HTTP Method | Safe | Idempotent |
---|---|---|
GET | Yes | Yes |
POST | No | No |
PUT | No | Yes |
DELETE | No | Yes |
PATCH | No | No |
Let's create a simple web application to demonstrate these five HTTP request methods. The code for this article is available over on GitHub.
Create a Spring Boot project #
First, we can use Spring Initializer to generate spring boot project quickly. The following dependecies are required:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.4.4</version>
</dependency>
Set up PostgreSQL & Spring Data JPA #
Spring Boot automatically configures a DataSource
for us when we add the postgresql dependency. Add the following properties to the application.properties
file and change the url, username and password.
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.username=caizhenhua
spring.datasource.password=123456
Add this property to the application.properties
file to automatically create tables for us. Never use this property in production and you should always let DBA review your DDL scripts before deployment.
spring.jpa.hibernate.ddl-auto=update
Create a user entity #
import java.time.LocalDateTime;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.PrePersist;
import javax.persistence.Table;
@Entity
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String username;
private String password;
private LocalDateTime createdDate;
@PrePersist
protected void onCreate() {
createdDate = LocalDateTime.now();
}
public User() {
}
// getters and setters
}
Now run the spring boot project, you should see your table t_user
is created in database postgres
.
Create service and repository #
IUserService:
import java.util.Map;
import java.util.Optional;
public interface IUserService {
Optional<User> getUserById(Long id);
User saveUser(User user);
User saveOrReplaceUserById(Long id, User newUser);
void deleteUser(User user);
User updateUser(Map<String, Object> updates, Long id);
}
UserService:
import java.util.Map;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@Service
public class UserService implements IUserService {
@Autowired
private UserRepository userRepository;
@Override
public Optional<User> getUserById(Long id) {
return userRepository.findById(id);
}
@Override
public User saveUser(User user) {
return userRepository.save(user);
}
@Override
public User saveOrReplaceUserById(Long id, User newUser) {
return this.getUserById(id)
.map(user -> {
user.setUsername(newUser.getUsername());
user.setPassword(newUser.getPassword());
return this.saveUser(user);
})
.orElseGet(() -> {
return this.saveUser(newUser);
});
}
@Override
public void deleteUser(User user) {
userRepository.delete(user);
}
@Override
public User updateUser(Map<String, Object> updates, Long id) {
User user = this.getUserById(id).get();
String username = (String) updates.get("username");
String password = (String) updates.get("password");
if (StringUtils.hasLength(username)) {
user.setUsername(username);
}
if (StringUtils.hasLength(password)) {
user.setPassword(password);
}
return this.saveUser(user);
}
}
UserRepository:
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends CrudRepository<User, Long> {
}
Define the ModelMapper bean #
import org.modelmapper.ModelMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Config {
@Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
}
Create a web controller #
UserController:
import java.util.Map;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
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;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private ModelMapper modelMapper;
// GET REQUEST
@GetMapping("/{id}")
public UserDTO getUser(@PathVariable("id") Long id) {
return convertToDTO(userService.getUserById(id).get());
}
// POST REQUEST
@PostMapping("")
public UserDTO createUser(@RequestBody UserDTO userDTO) {
User user = convertToEntity(userDTO);
return convertToDTO(userService.saveUser(user));
}
// PUT REQUEST
@PutMapping("{id}")
public UserDTO createOrReplaceUser(@RequestBody UserDTO userDTO, @PathVariable("id") Long id) {
return convertToDTO(userService.saveOrReplaceUserById(id, convertToEntity(userDTO)));
}
// DELETE REQUEST
@DeleteMapping("{id}")
public void deleteUser(@PathVariable("id") Long id) {
userService.deleteUser(userService.getUserById(id).get());
}
// PATCH REQUEST
@PatchMapping("{id}")
public UserDTO updateUser(@RequestBody Map<String, Object> updates, @PathVariable("id") Long id) {
return convertToDTO(userService.updateUser(updates, id));
}
private UserDTO convertToDTO(User user) {
return modelMapper.map(user, UserDTO.class);
}
private User convertToEntity(UserDTO userDTO) {
return modelMapper.map(userDTO, User.class);
}
}
Test with Spring MockMvc #
Insert a new record into t_user
table:
INSERT INTO t_user (id, created_date, username, password) VALUES (1, CURRENT_TIMESTAMP, 'caizhenhua', '123');
UserControllerTest:
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import com.fasterxml.jackson.databind.ObjectMapper;
@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void getUserByIdAPI() throws Exception {
this.mockMvc.perform(get("/user/1"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().json("{'username': 'caizhenhua', 'password': '123'}"));
}
@Test
public void createUserAPI() throws Exception {
UserDTO userDTO = new UserDTO();
userDTO.setUsername("admin");
userDTO.setPassword("123");
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(userDTO);
this.mockMvc.perform(post("/user")
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().json("{'username': 'admin', 'password': '123'}"));
}
@Test
public void createOrReplaceUserAPI() throws Exception {
UserDTO userDTO = new UserDTO();
userDTO.setUsername("test");
userDTO.setPassword("456");
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(userDTO);
this.mockMvc.perform(put("/user/2")
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().json("{'username': 'test', 'password': '456'}"));
}
@Test
public void deleteUserAPI() throws Exception {
this.mockMvc.perform(delete("/user/3"))
.andDo(print())
.andExpect(status().isOk());
}
@Test
public void updateUserAPI() throws Exception {
UserDTO userDTO = new UserDTO();
userDTO.setUsername("test");
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(userDTO);
this.mockMvc.perform(patch("/user/5")
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().json("{'username': 'test', 'password': '123'}"));
}
}
- Previous: Java Heap Pollution
- Next: Java 11 HTTP Client Examples