Software testing / test development丨RESTful API design and implementation based on Spring Boot

RESTful is a specification, and an Api that conforms to RESTful is a RESTful Api. Simply put, networkable devices use the HTTP protocol to operate server resources with URI identifiers through GET, POST, DELETE, PUT, and PATCH, and return resource information in a unified format, including JSON, XML, CSV, ProtoBuf, and other formats.

The core idea of RESTful is that the data operation instructions issued by the client are all “verb + object” structure. For example, for the command GET /case, GET is a verb and /case is an object.

Introduction to RESTful API

  • Simple and fast: RESTful simplifies bad design by developers due to its resource-oriented interface design and operation abstraction, and also makes maximum use of the original application protocol design concept of http.

  • The RESTful architecture follows the principle of unified interface, no matter what kind of resources, access to resources by using the same interface. Interfaces should use standard HTTP methods like GET , PUT and POST and follow the semantics of those methods.

Design specification

HTTP request verb

Commonly used verbs are the following 5

HTTP response status code

Spring Boot implements RESTful API

We can implement RESTful API through Spring Boot annotations.

Spring Boot annotations

What needs to be written now is to add, delete, modify and query a user. The following table is a comparison table between non-RESTful and standard RESTful.

Below we focus on the following two pairs of annotations.

Controller and RestController

Controller is generally used in application scenarios with return interface. For example, the management background is developed using template technology such as thymeleaf, and it is necessary to directly return the Model object from the background to the foreground, so it needs to be annotated with Controller.

RestController is generally used in application scenarios with only interfaces. For example, when developing a project with front and back ends separated, and requesting the server-side interface through Ajax, the interface will be annotated uniformly with RestController.

Note that RestController is a subset of Controller. RestController is a new annotation added after Spring4. From the source code of the RestController annotation, we can see that RestController is a combination of the two annotations of Controller and ResponseBody, that is, Controller=RestController + ResponseBody.

RestController annotation source code

@Target({ElementType.TYPE})</code><code>@Retention(RetentionPolicy.RUNTIME)</code><code>@Documented</code><code>@Controller</code><code>@ResponseBody</code><code>public @interface RestController {<!-- --></code><code> @AliasFor(</code><code> annotation = Controller.class</code><code> )</code><code> String value() default "";</code><code>}

RequestMapping and GetMapping

RequestMapping and GetMapping/PostMapping/PutMapping/DeleteMapping have the same function and can be replaced with each other. The latter is a simplified version of the former.

GetMapping is actually equal to setting the method attribute of RequestMapping annotation to GET, PostMapping is actually equal to setting the method attribute of RequestMapping annotation to POST, PutMapping and DeleteMapping are actually equal to setting the method attribute of RequestMapping annotation to PUT and DELETE respectively.

That is to say, GetMapping, PostMapping, PutMapping, and DeleteMapping are subsets of RequestMapping.

Let’s take a look at the source code of RequestMapping:

@Target({ElementType.TYPE, ElementType.METHOD})</code><code>@Retention(RetentionPolicy.RUNTIME)</code><code>@Documented</code><code>@Mapping</code><code>public @interface RequestMapping {<!-- --></code><code> String name() default "";</code>
<code> //Request URI</code><code> @AliasFor("path")</code><code> String[] value() default {};</code><code> @AliasFor( "value")</code><code> String[] path() default {};</code><code> //Request type, such as GET, POST, PUT, DELETE, etc.</code><code> RequestMethod[] method() default {};</code><code> //The request parameter must contain certain parameter values before it can be processed by this method. </code><code> String[] params() default {};</code><code> //Request parameters must contain certain specified header values in order for this method to process the request. </code><code> String[] headers() default {};</code><code> //The requested content type (Content-Type), such as application/json, text/html;</code><code> String[] consumes() default {};</code><code> //The content type of the response, only if the (Accept) type in the request request header contains the specified type; </code><code> String[] produces() default {};</code><code>}

Example explanation:

API form

sample code

  • Add 2 new files: dto/UserDto.java and controller/HogwartsTestUserController.java , where the UserController class includes 4 operations for adding, deleting, modifying, and checking users.

public class UserDto {<!-- --></code>
<code> private String name;</code><code> private String pwd;</code>
<code> public String getName() {<!-- --></code><code> return name;</code><code> }</code>
<code> public void setName(String name) {<!-- --></code><code> this.name = name;</code><code> }</code>
<code> public String getPwd() {<!-- --></code><code> return pwd;</code><code> }</code>
<code> public void setPwd(String pwd) {<!-- --></code><code> this.pwd = pwd;</code><code> }</code><code>}</pre >
<p></p>
<pre>/**</code><code> * RESTful API style example to operate resource user</code><code> * This example does not use a database, nor does it use a service class to assist completion, all operations are in this Completed in class</code><code> * */</code><code>@Api(tags = "Hogwarts Testing Institute - User Management Module", hidden = true)</code><code>@RestController</code><code>@RequestMapping("/api/user")</code><code>public class HogwartsTestUserController {<!-- --></code>
<code> /**</code><code> * Query user list, return a JSON array</code><code> * */</code><code> @ApiOperation("query user list") </code><code> @GetMapping("/users")</code><code> @ResponseStatus(HttpStatus.OK)</code><code> public Object getUsers(){<!-- -- ></code><code> List<UserDto> list = getData();</code><code> return list;</code><code> }</code>
<code> /**</code><code> * Query user information and return a new JSON object</code><code> * */</code><code> @ApiOperation("query user information\ ")</code><code> @GetMapping("/users/{id}")</code><code> @ResponseStatus(HttpStatus.OK)</code><code> public Object getUser(@PathVariable ("id") Long id){<!-- --></code>
<code> if(Objects.isNull(id)){<!-- --></code><code> return null;</code><code> }</code>
<code> List<UserDto> list= getData();</code><code> UserDto userDto = getUserDto(id, list);</code>
<code> return userDto;</code><code> }</code>
<code> /**</code><code> * Add new user</code><code> * */</code><code> @ApiOperation("Add user")</code><code> @PostMapping("/users")</code><code> @ResponseStatus(HttpStatus.CREATED)</code><code> public Object addUser(@RequestBody UserDto user){<!-- --> </code>
<code> List<UserDto> list= getData();</code><code> list.add(user);//Simulate adding data to the list</code><code> return user;</code><code> }</code>
<code> /**</code><code> * Edit User</code><code> * */</code><code> @ApiOperation("Edit User")</code><code> @PutMapping("/users/{id}")</code><code> @ResponseStatus(HttpStatus.CREATED)</code><code> public Object editUser(@PathVariable("id") Long id ,@RequestBody UserDto user){<!-- --></code><code> List<UserDto> list = getData();</code><code> for (UserDto userDto:list) {<!-- --></code><code> if(id.equals(userDto.getId())){<!-- --></code><code> userDto = user;</code><code> break ;</code><code> }</code><code> }</code>
<code> return user;</code><code> }</code>
<code> /**</code><code> * delete user</code><code> * */</code><code> @ApiOperation("delete user")</code><code> @DeleteMapping("/users/{id}")</code><code> @ResponseStatus(HttpStatus.NO_CONTENT)</code><code> public Object deleteUser(@PathVariable("id") Long id ){<!-- --></code><code> List<UserDto> list = getData();</code><code> UserDto userDto = getUserDto(id, list);</code><code> return userDto;</code><code> }</code>
<code> /**</code><code> * Simulated data</code><code> * */</code><code> private List<UserDto> getData(){<!-- --></code><code> List<UserDto> list=new ArrayList<>();</code>
<code> UserDto userDto = new UserDto();</code><code> userDto.setId(1L);</code><code> userDto.setName("admin");</code><code> userDto.setPwd("admin");</code><code> list.add(userDto);</code>
<code> userDto = new UserDto();</code><code> userDto.setId(2L);</code><code> userDto.setName("HogwartsTest1");</code><code> userDto .setPwd("HogwartsTest1");</code><code> list.add(userDto);</code>
<code> userDto = new UserDto();</code><code> userDto.setId(3L);</code><code> userDto.setName("HogwartsTest2");</code><code> userDto .setPwd("HogwartsTest2");</code><code> list.add(userDto);</code>
<code> userDto = new UserDto();</code><code> userDto.setId(4L);</code><code> userDto.setName("HogwartsTest3");</code><code> userDto .setPwd("HogwartsTest3");</code><code> list.add(userDto);</code>
<code> return list;</code><code> }</code>
<code> /**</code><code> * Simulate querying the data in the list according to id</code><code> * @param id</code><code> * @param list</code><code> * @return</code><code> */</code><code> private UserDto getUserDto( Long id, List<UserDto> list) {<!-- --></code><code> UserDto UserDto = null;</code><code> for (UserDto user : list) {<!-- --></code><code> if (id.equals(user.getId())) {<!-- --></code><code> UserDto = user;</code><code> break;</code><code> }</code><code> }</code><code> return UserDto;</code><code> }</code><code>}

Test with Postman

Get all resources Get all users

GET http://127.0.0.1:8081/api/user/users/

response parameter

[ { "id": 1, "name": "admin", "pwd": "admin" }, { "id": 2, "name ": "HogwartsTest1", "pwd": "HogwartsTest1" }, { "id": 3, "name": "HogwartsTest2", "pwd": " "HogwartsTest2" }, { "id": 4, "name": "HogwartsTest3", "pwd": "HogwartsTest3" }]</code><code>Copy Code< /pre>
<p>Get a single resource Get a user</p>
<p>GET http://127.0.0.1:8081/api/user/users/3</p>
<p>Add a resource Add a user</p>
<p>POST http://127.0.0.1:8081/api/user/users</p>
<p>request parameters</p>
<pre>{<!-- --></code><code> "id": 4,</code><code> "name": "HogwartsTest5",</code><code> "pwd": "HogwartsTest5"</code><code>}

edit update a resource

PUT http://127.0.0.1:8081/api/user/users/3

request parameters

{<!-- --></code><code> "name": "HogwartsTest6",</code><code> "pwd": "HogwartsTest6"</code><code>}

delete a resource

DELETE http://127.0.0.1:8081/api/user/users/3

Common configuration items

The following introduces some common configuration items of Spring Boot. Through these common configuration items, we can modify some default configurations of Spring Boot.

Modify the service default port:

server:</code><code>port: 8093

Specify the service name:

spring:</code><code> application:</code><code> name: aitest

Multi-environment configuration

spring: profiles: active: dev

Create four sets of configuration file environments: application-dev.yml, application-test.yml, application-uat.yml, and application-prod.yml as shown in the figure above. We set the service port numbers to 8091/8092 in the four sets of configuration files. /8093/8094. Then start the service, and you can see that the port number of the service will be consistent with the environment configuration information activated in application.yml.

The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge. Java skill treeHomepageOverview 108548 people are studying systematically