One, foreword

The separation of front and back ends is the mainstream of today’s service forms. How to design a good RESTful API and how to enable front-end partners to deal with the standard Response JSON data structure are of great importance. In order to enable front-end partners to have better logical display and page interaction processing, Each RESTful request should contain the following information:

The name of the describe
status Status code: indicates whether the request is successful or not. The HTTP status remains the same
msg Error messages, which correspond to error codes, more specifically describe the exception information
exception Exception information, provides detailed exception stack information
data Return the result, usually the JSON data corresponding to the Bean object, which is usually declared as a generic type to accommodate the different return value types

Second, the implementation

1. Rely on

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
</dependency>

<dependency>
	<groupId>cn.hutool</groupId>
	<artifactId>hutool-all</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-test</artifactId>
	<scope>test</scope>
</dependency>
Copy the code

2. Define generic return classes

import java.io.Serializable;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Getter
@ToString
@Builder
@EqualsAndHashCode(callSuper = false)
@AllArgsConstructor
@NoArgsConstructor
public class ApiResponses<T> implements Serializable{

	private static final long serialVersionUID = -6548932393957418417L;

	@Builder.Default
	private int status = 200;

	private String code;

	private String msg;
	
	private String exception;

	private T data;
}
Copy the code

3. The controller

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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.gitee.example.model.User;
import com.gitee.web.api.ApiResponses;

/** * Interface class */
@RestController
@RequestMapping(value = "/users2")
public class UserController2 {

	// Create thread-safe Map
	private static Map<Long, User> users = Collections.synchronizedMap(new HashMap<Long, User>());

	@GetMapping
	public ApiResponses<List<User>> getUserList() {
		// Handle the GET request for "/users/" to GET the list of users
		// You can also use @requestParam to pass parameters from the page for query criteria or page-turning information
		List<User> r = new ArrayList<>(users.values());
		return ApiResponses.<List<User>>builder().data(r).msg("Query list succeeded").build();
	}
 
	@PostMapping
	public ApiResponses<String> postUser(@RequestBody User user) {
		// Handles POST requests for "/users/", used to create users
		In addition to the @requestBody binding parameter, you can also pass parameters from the page via @requestParam
		users.put(user.getId(), user);
		return ApiResponses.<String>builder().msg("New success").build();
	}

	@GetMapping("/{id}")
	public ApiResponses<User> getUser(@PathVariable Long id) {
		// Handle the GET request for "/users/{id}", which is used to GET the User information for the id value in the URL
		// The ID in the URL can be bound to the parameter of the function via @pathvariable
		return  ApiResponses.<User>builder().data(users.get(id)).msg("Query successful").build();
	}

	@PutMapping("/{id}")
	public ApiResponses<String> putUser(@PathVariable Long id, @RequestBody User user) {
		// Handle the PUT request for "/users/{id}" to update the User information
		User u = users.get(id);
		u.setName(user.getName());
		u.setAge(user.getAge());
		users.put(id, u);
		return ApiResponses.<String>builder().msg("Update successful").build();
	}

	@DeleteMapping("/{id}")
	public ApiResponses<String> deleteUser(@PathVariable Long id) {
		// Handle the DELETE request for "/users/{id}", which is used to DELETE the User
		users.remove(id);
		return ApiResponses.<String>builder().msg("Deleted successfully").build(); }}Copy the code

4. The Mock test

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
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.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import java.nio.charset.StandardCharsets;
import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
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.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.RequestBuilder;

import com.gitee.example.model.User;
import com.gitee.web.api.ApiResponses;

import cn.hutool.core.lang.TypeReference;
import cn.hutool.json.JSONUtil;

@AutoConfigureMockMvc
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ApplicationTests2 {
   
	@Autowired
    private MockMvc mvc;

 
	@Test
    public void testUserController2(a) throws Exception {
        UserController / / test
        RequestBuilder request;

        // 1, the user list should be empty
        request = get("/users2/");
        MvcResult result = mvc.perform(request)
                .andExpect(status().isOk())
                .andReturn();
        String content = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
        ApiResponses<List<User>> response = JSONUtil.toBean(content, new TypeReference<ApiResponses<List<User>>>() {}, true);
        assertThat(response.getData().size(), is(0));
        assertThat(response.getMsg(), is("Query list succeeded"));

        // 2, post submits a user
        User puser = new User();
        puser.setId(1L);
        puser.setName("Test Master");
        puser.setAge(20);
        
        request =  post("/users2/")
        .contentType(MediaType.APPLICATION_JSON)
        .content(JSONUtil.toJsonStr(puser));
        
        result = mvc.perform(request)
                .andExpect(status().isOk())
                .andReturn();
        content = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
        ApiResponses<String> response1 = JSONUtil.toBean(content, new TypeReference<ApiResponses<String>>() {}, true);
        assertThat(response1.getMsg(), is("New success"));

        // 3, get get user list, should have just inserted data
        request = get("/users2/");
        result = mvc.perform(request)
                .andExpect(status().isOk())
                .andReturn();
        content = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
        ApiResponses<List<User>> response2 = JSONUtil.toBean(content, new TypeReference<ApiResponses<List<User>>>() {}, true);
        assertThat(response2.getData().size(), is(1));
        assertThat(response2.getMsg(), is("Query list succeeded"));

        // 4. Put Changes the user whose ID is 1
        puser.setName("The Ultimate Test master.");
        puser.setAge(30);
        request =  put("/users2/1")
                .contentType(MediaType.APPLICATION_JSON)
                .content(JSONUtil.toJsonStr(puser));
        
        result = mvc.perform(request)
                .andExpect(status().isOk())
                .andReturn();
        content = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
        ApiResponses<String> response3 = JSONUtil.toBean(content, new TypeReference<ApiResponses<String>>() {}, true);
        assertThat(response3.getMsg(), is("Update successful"));

        // get a user whose id is 1
        request = get("/users2/1");
        result = mvc.perform(request)
                .andExpect(status().isOk())
                .andReturn();
        content = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
        ApiResponses<User> response4 = JSONUtil.toBean(content, new TypeReference<ApiResponses<User>>() {},true);
        User user = response4.getData();
        assertThat(user.getId(), is(1L));
        assertThat(user.getName(), is("The Ultimate Test master."));

        // 6. Del Delete user 1
        request = delete("/users2/1");
        result = mvc.perform(request)
                .andExpect(status().isOk())
                .andReturn();
        content = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
        ApiResponses<String> response5 = JSONUtil.toBean(content, new TypeReference<ApiResponses<String>>() {}, true);
        assertThat(response5.getMsg(), is("Deleted successfully"));

        // select * from user
        request = get("/users2/");
        result = mvc.perform(request)
                .andExpect(status().isOk())
                .andReturn();
        content = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
        ApiResponses<List<User>> response6 = JSONUtil.toBean(content, new TypeReference<ApiResponses<List<User>>>() {}, true);
        assertThat(response6.getData().size(), is(0));
        assertThat(response6.getMsg(), is("Query list succeeded")); }}Copy the code