background

Is something so simple worth writing an article about?

When I was working on a project last year, as more and more people were working on it, it quickly became clear that everyone had their own style. Considering team cooperation, it is suggested to formulate relevant norms to make everyone’s pace consistent, which is also conducive to the promotion efficiency of the project. The implementation was particularly divisive (unsurprisingly), with the Http API being the most controversial, with two major issues identified:

  • Many students think that RESTful apis are good (advanced), but why is it good?

  • A lot of students think they know what RESTful apis look like, but when they sit down and talk, they find that everyone’s different, and no one can convince the other?

In the end, due to the project cycle, we did not focus on this matter. Under the guise of business priority, we first promoted the project according to their own style, and gradually established norms in the process of practice.

Later, when reviewing the code, I also found an interesting phenomenon: Even for the same student, the API of RESTful style written by him is actually inconsistent, which mainly shows two phenomena:

  • The design of API URLS and parameters is obviously different in similar business scenarios. I don’t know why, but it may be related to my mood when writing specific API.

  • In some business scenarios, API design has obviously deviated from RESTful. This student said, “Otherwise, business logic can not be implemented”.

After talking to a number of students, I came to the conclusion that it was impossible for students who thought they were RESTful to create a set of specifications for what scenarios and how apis should be designed based on their own understanding.

Such a popular thing, should be a high degree of standardization (everyone’s consensus), why will there be such a phenomenon? Until I saw an article in the last few days:

REST stands for “Representational State Transfer,” described by Roy Fielding in his dissertation. Sadly, that dissertation is not widely read, and so many people have their own idea of what REST is, leading to a lot of confusion and disagreement.

REST was proposed in a foreign paper (2000). However, this paper should be many people have not actually read, everyone is based on the Internet “bits and pieces” to understand, so it has led to a lot of misunderstanding and inconsistency.

While the paper is a bit long, and I don’t think many people will actually read it (myself included), I recommend a comprehensive article from Microsoft that serves as a basic guide to getting started and practicing RESTful apis.

The subsequent chapters of this article build on this article and focus on key technical points of how to build RESTful apis using SpringBoot, which should be ignored for those with relevant experience.

Create a SpringBoot project/module

Create Maven Project (SpringBoot) with Idea; Create another module, named API, for building RESTful apis, and only pom.xml is listed here.

SpringBoot pom.xml



      

<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/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>tech.exchange</groupId>

<artifactId>springboot</artifactId>

<packaging>pom</packaging>

<version>0.1</version>

<modules>

<module>api</module>

</modules>

<properties>

<spring-boot.version>2.6.1</spring-boot.version>

<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>

<maven-assembly-plugin.version>3.3.0</maven-assembly-plugin.version>

<java.version>17</java.version>

<encoding>UTF-8</encoding>

</properties>

<dependencyManagement>

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>${spring-boot.version}</version>

<type>pom</type>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

<version>${spring-boot.version}</version>

</dependency>

</dependencies>

</dependencyManagement>

<build>

<plugins>

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-compiler-plugin</artifactId>

<version>${maven-compiler-plugin.version}</version>

<configuration>

<source>${java.version}</source>

<target>${java.version}</target>

<encoding>${encoding}</encoding>

</configuration>

</plugin>

<plugin>

<artifactId>maven-assembly-plugin</artifactId>

<version>${maven-assembly-plugin.version}</version>

<configuration>

<descriptors>

<! --suppress UnresolvedMavenProperty -->

<descriptor>

${maven.multiModuleProjectDirectory}/src/assembly/package.xml

</descriptor>

</descriptors>

</configuration>

<executions>

<execution>

<id>make-assembly</id>

<phase>package</phase>

<goals>

<goal>single</goal>

</goals>

</execution>

</executions>

</plugin>

</plugins>

</build>

</project>

Copy the code

A brief overview of what the various parts of POM.xml mean:

  1. groupId/artifactId/packaging/version

<groupId>tech.exchange</groupId>

<artifactId>springboot</artifactId>

<packaging>pom</packaging>

<version>0.1</version>

Copy the code

Used to declare the organization, name, packaging, and version of the project.

  1. modules

<modules>

<module>api</module>

</modules>

Copy the code

Used to declare multiple modules within a project (multi-module project), including only one module here: API.

  1. properties

<properties>.</properties>

Copy the code

Use to declare properties (values) in the project POM.xml that may be used multiple times or need to be set uniformly, such as the SpringBoot version number.

  1. dependencyManagement/dependencies

<dependencyManagement>

<dependencies>.</dependencies>

</dependencyManagement>

Copy the code

It is used to declare the name and version of the Jar that the project needs to use. The module of the project only needs to declare the name of the Jar that the project needs to use. The version is specified by the project.

  1. build/plugins

<build>

<plugins>.</plugins>

</build>

Copy the code

Used to declare plug-ins needed for project or module construction (compilation, packaging, or otherwise).

api pom.xml



      

<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/xsd/maven-4.0.0.xsd">

<parent>

<groupId>tech.exchange</groupId>

<artifactId>springboot</artifactId>

<version>0.1</version>

</parent>

<modelVersion>4.0.0</modelVersion>

<artifactId>api</artifactId>

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

</dependencies>

</project>

Copy the code
  1. artifactId

<artifactId>api</artifactId>

Copy the code

Used to declare the module name.

  1. dependencies/dependency

<dependencies>

<dependency>.</dependency>

</dependencies>

Copy the code

Used to declare dependencies (jars) that the module needs to use specifically.

Create RESTful API applications

Create a class named Main:


package tech.exchange.springboot.api;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

/ * * *@author yurun

*/

@SpringBootApplication

public class Main {

public static void main(String[] args) { SpringApplication.run(Main.class, args); }}Copy the code

@SpringBootApplication

@SpringBootApplication is a collection of three annotations:

@SpringBootApplication = @Configuration + @EnableAutoConfiguration + @ComponentScan

What it does is tell Spring to automatically load various beans (components or configurations) into the container using Main as an entry point, resulting in a SpringBoot application that receives and responds to external requests.

These beans come from three aspects:

@Configuration

Tags the class as a source of bean definitions for the application context.

Load our custom Beans in Main (custom Beans are not included in this article’s example).

@EnableAutoConfiguration

Tells Spring Boot to start adding beans based on classpath settings, other beans, and various property settings.

Load required Beans based on the classpath, other Beans, or property configuration. For example, web-related Beans are automatically loaded if the example includes a dependency on spring-boot-starter-web.

@ComponentScan

Tells Spring to look for other components, configurations, and services in the tech/exchange/springboot/API package.

Scanning Package (Package) path: tech/exchange/springboot/API, the Beans under loading Package.

SpringApplication.run


SpringApplication.run(Main.class, args);

Copy the code

By default, you will see the following output when you start the SpringBoot application:

The 2021-12-02 14:44:15. 58552-537 the INFO [main] tech. Exchange. Springboot. API. Main: Starting Main using Java 17.0.1 on bogon with PID 58552 (/Users/yurun/workspace/tech-exchange/springboot/api/target/classes started by yurun in / Users/yurun/workspace/tech - exchange/springboot) 14:44:15 2021-12-02. 58552-538 the INFO [main] tech.exchange.springboot.api.Main : No active profile set, falling back to default profiles: The default 14:44:16 2021-12-02. 58552-160 the INFO [main] O.S.B.W.E mbedded. Tomcat. TomcatWebServer: Tomcat initialized with port(s): 8080 (HTTP) 14:44:16 2021-12-02. 58552-170 the INFO [main] o.a pache, catalina. Core. StandardService: Starting the service [Tomcat] 2021-12-02 14:44:16. 170 INFO 58552 - [the main] org. Apache. Catalina. Core. StandardEngine: Starting Servlet engine: [Apache Tomcat/9.0.55] 2021-12-02 14:44:16.213 INFO 58552 -- [main] O.A.C.C.C. [Tomcat].[localhost].[/] : Initializing Spring Embedded WebApplicationContext 2021-12-02 14:44:16.214 INFO 58552 -- [main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: Initialization Completed in 633 MS 2021-12-02 14:44:16.436 INFO 58552 -- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (HTTP) with the context path '14:44:16 2021-12-02. 58552-443 the INFO tech. [the main] exchange. Springboot. API. Main: Started Main in 1.209 seconds (JVM running for 1.584)Copy the code

8080 (HTTP) indicates that the application instance port number is 8080 and supports HTTP requests.

Rest Controller

Currently, the application is an empty application and cannot actually receive any requests or responses. HTTP requests or responses in SpringBoot need to be implemented through controllers, and a Controller can support (contain) one or more implementations of HTTP requests or responses, i.e. one or more API implementations.


package tech.exchange.springboot.api.controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

/ * * *@author yurun

*/

@RestController

@RequestMapping("/hello")

public class HelloController {}Copy the code

@RestController

@restController is used to identify HelloController as a Controller, and SpringBoot applications are automatically loaded into the container when started.

@RequestMapping

HelloController is a collection of apis that can contain multiple implementations, such as:


/hello/a1

/hello/a2

/hello/a3

......

Copy the code

You can use @requestMapping to identify the parent path of these API request paths, for example, /hello. Internal API request paths do not need to contain the parent path, but use /a1, /a2, or/A3.

Get/Post/Put/Patch/Delete

Currently, HelloController is an empty Controller that contains no implementation of the API.

RESTfull API involved five types of request: Get/Post/Put/Patch/Delete, 3 types of parameters: request path parameters, the request parameters and body. Each request type simulates the implementation of an API to demonstrate the implementation of the API and how each parameter type is used.

Get

Add Method get to HelloController:


@GetMapping("/get/{name1}")

public String get(@PathVariable String name1, @RequestParam(defaultValue = "name2dv") String name2) {

return "hello " + name1 + "" + name2;

}

Copy the code

@GetMapping

@getMapping is used to identify that method GET responds only to HTTP GET requests and the request path is /hello/get/{name1}, where {name1} is the request path parameter name. In actual requests, you need to replace it with a specific parameter value, for example, value1.

@PathVariable


@PathVariable String name1

Copy the code

@pathvariable is used to identify request method parameters and receive request path parameters. If the request path is /hello/value1, the parameter value1 is passed to the name1 parameter of the request method get when the request is executed.

The request path parameter is mandatory by default (it cannot be modified). You must enter this parameter when you initiate a request. Otherwise, the request fails.

@RequestParam


@RequestParam(defaultValue = "name2dv") String name2

Copy the code

RequestParam is used to identify request method parameters and receive request parameters. Suppose the request path is /hello? Name2 =value2, value2 is passed to the name2 parameter of the request method get. If the request path is /hello, the request method get parameter name2 is set to the default name2DV when executing the request.

Request parameters are mandatory by default (which can be modified with the annotation attribute required) and must be filled in when the request is initiated. If defaultValue is set, use the defaultValue instead of the defaultValue when sending a request. Otherwise, the request fails.

Invoke the sample

Request: curl http://localhost:8080/hello/get/value1 response: hello value1 name2dv request: curl http://localhost:8080/hello/get/value1? Name2 =value2 Response: Hello value1 value2Copy the code

The parameter names of the request path parameter and the request method parameter must be the same. If they are not the same, they need to be specified through the annotation attribute (the same below).

Parameter types must be compatible with those declared by the request method (the same below).

Request path parameters and request method parameters can be zero or more (same below);

The application scenarios of request body parameters in Get scenarios are few and will not be discussed in this article.

Post

In Post, the request path parameters and request parameters are used in the same way as Get, which will not be described again. Only the request body parameters are used. The usage of Request body parameters depends on the specific value of the HTTP Request Header content-type. This article only discusses the most common Type: application/ JSON.

The request body parameters need to be passed in JSON to initiate the request, and the request method needs to receive the request body parameter values through the class.


@PostMapping("/post")

public String post(@RequestBody PostParams params) {

return "hello " + params.getName1() + "" + params.getName2();

}

Copy the code

@PostMapping

PostMapping indicates that the post method responds only to HTTP POST requests and the request path is /hello/ POST.

@RequestBody


@RequestBody PostParams params

Copy the code

@requestbody is used to identify the request method parameters and receive the RequestBody parameters (JSON).

Assume the request body parameters:


{"name1": "value1"."name2": "value2"}

Copy the code

We need to create a class for the receiver parameters:


public class PostParams {

private String name1;

private String name2 = "name2dv"; public String getName1() { return name1; } public void setName1(String name1) { this.name1 = name1; } public String getName2() { return name2; } public void setName2(String name2) { this.name2 = name2; }}Copy the code

Each JSON field is assigned by name to the class instance (PARAM) field when the request is executed. If name1 does not exist in JSON, name1 is null. If name2 does not exist in JSON, name2 is name2DV. If some fields in JSON do not exist in the class, they will be ignored.

Invoke the sample

Curl -h "content-type :application/json" -x POST --data '{"name1": "value1", "name2": "Value2"} 'http://localhost:8080/hello/post response: hello value1 value2 request: Curl -h "content-type: application/json" - X POST - data '{}' http://localhost:8080/hello/post response: hello null name2dv request: curl -H "Content-Type:application/json" -X POST --data '{"name1": "value1", "name3": "Value2"} 'http://localhost:8080/hello/post response: hello value1 name2dvCopy the code

Request body parameters can only be zero or one (same below);

The request body parameter field type must be compatible with the class field type (same below).

Request path parameters, request parameters, and request body parameters can be mixed (similarly below).

Put


@PutMapping("/put")

public String put(@RequestBody PostParams params) {

return "hello " + params.getName1() + "" + params.getName2();

}

Copy the code

@PutMapping

@putMapping indicates that the put method responds only to HTTP PUT requests and the request path is /hello/ PUT.

The rest is the same as above and will not be repeated.

Patch


@PatchMapping("/patch")

public String patch(@RequestBody PostParams params) {

return "hello " + params.getName1() + "" + params.getName2();

}

Copy the code

@PatchMapping

PatchMapping indicates that method patch responds only to HTTP patch requests, and the request path is /hello/patch.

The rest is the same as above and will not be repeated.

Delete


@DeleteMapping("/delete")

public String delete(@RequestBody PostParams params) {

return "hello " + params.getName1() + "" + params.getName2();

}

Copy the code

@DeleteMapping

@deletemapping is used to indicate that the method delete responds only to HTTP DELETE requests, and the request path is /hello/delete.

The rest is the same as above and will not be repeated.

conclusion

This article introduces an article about the RESTfull API, and based on this, demonstrates a complete process of using SpringBoot to build RESTfull API applications, core configuration and annotations are also provided, hopefully helpful.

The attached

Github.com/tech-exchan…

Github.com/tech-exchan…