前言
大家好!我是sum墨,一個一線的底層碼農(nóng),平時喜歡研究和思考一些技術(shù)相關(guān)的問題并整理成文,限于本人水平,如果文章和代碼有表述不當之處,還請不吝賜教。
作為一名從業(yè)已達六年的老碼農(nóng),我的工作主要是開發(fā)后端Java業(yè)務(wù)系統(tǒng),包括各種管理后臺和小程序等。在這些項目中,我設(shè)計過單/多租戶體系系統(tǒng),對接過許多開放平臺,也搞過消息中心這類較為復(fù)雜的應(yīng)用,但幸運的是,我至今還沒有遇到過線上系統(tǒng)由于代碼崩潰導(dǎo)致資損的情況。這其中的原因有三點:一是業(yè)務(wù)系統(tǒng)本身并不復(fù)雜;二是我一直遵循某大廠代碼規(guī)約,在開發(fā)過程中盡可能按規(guī)約編寫代碼;三是經(jīng)過多年的開發(fā)經(jīng)驗積累,我成為了一名熟練工,掌握了一些實用的技巧。
接口參數(shù)是導(dǎo)致很多BUG產(chǎn)生的始作俑者,原因在于接口參數(shù)有3多:接口參數(shù)的取值地方多,如查詢參數(shù)(Query Parameters)、路徑參數(shù)(Path Parameters)、請求體(Request Body)等;數(shù)據(jù)類型多,如數(shù)字、字符、日期、文件等;判斷情況多,如空值判斷、格式判斷、大小判斷等;
本文參考項目源碼地址:summo-springboot-interface-demo
由于文章經(jīng)常被抄襲,開源的代碼甚至被當成收費項,所以源碼里面不是全部代碼,有需要的同學(xué)可以留個郵箱,我給你單獨發(fā)!
一、接口參數(shù)的取值
1. 放在查詢參數(shù)和請求體里
a、方法參數(shù)
示例代碼如下:
@GetMapping("/testParams1")
public ResponseEntity<String> testParams1(String param1, Integer param2) {
return ResponseEntity.ok(MessageFormat.format("param1:[{0}];param2:[{1}]", param1, param2));
}
調(diào)用請求:http://localhost:8080/testParams1?param1=111¶m2=222
返回如下:

沒啥坑。
b、請求對象
GET請求
示例代碼如下
@GetMapping("/testParams2")
public ResponseEntity<String> testParams2(ParamsReq paramsReq) {
return ResponseEntity.ok(MessageFormat.format("param1:[{0}];param2:[{1}]", paramsReq.getParam1(), paramsReq.getParam2()));
}
ParamsReq.java
public class ParamsReq {
private String param1;
private String param2;
public ParamsReq() {
}
public ParamsReq(String param1, String param2) {
this.param1 = param1;
this.param2 = param2;
}
public String getParam1() {
return param1;
}
public void setParam1(String param1) {
this.param1 = param1;
}
public String getParam2() {
return param2;
}
public void setParam2(String param2) {
this.param2 = param2;
}
@Override
public String toString() {
return "ParamsReq{" +
"param1='" + param1 + '\'' +
", param2='" + param2 + '\'' +
'}';
}
}
調(diào)用請求:http://localhost:8080/testParams2?param1=111¶m2=222
返回如下:

這種有一個坑,Spring默認使用無參構(gòu)造函數(shù)來實例化對象,所以ParamsReq不能是接口、抽象類等特殊類。
POST請求
示例代碼如下:
@PostMapping("/testParams3")
public ResponseEntity<String> testParams3(ParamsReq paramsReq) {
return ResponseEntity.ok(MessageFormat.format("param1:[{0}];param2:[{1}]", paramsReq.getParam1(), paramsReq.getParam2()));
}
ParamsReq類代碼同上
- 調(diào)用方式1:參數(shù)放在鏈接上

和GET請求類似,沒啥坑。
-
調(diào)用方式2:放在Form表單中,content-type為application/x-www-form-urlencoded
image.png
沒啥坑。
- 調(diào)用方式3:放在body參數(shù)中,content-type為application/json

這里有坑了,當content-type為application/json時,接口參數(shù)取值為空。這時就需要在參數(shù)前加上一個注解:
@RequestBody,原因是通過@RequestBody注解,Spring Boot可以自動地將請求體中的JSON數(shù)據(jù)轉(zhuǎn)換為Java對象,從而方便地進行數(shù)據(jù)的處理和轉(zhuǎn)換。如果不加@RequestBody注解,Spring Boot默認會將請求體中的JSON數(shù)據(jù)作為普通的表單數(shù)據(jù)來處理,而不會自動轉(zhuǎn)換為Java對象。:
改成這樣就行:
public ResponseEntity<String> testParams3(@RequestBody ParamsReq paramsReq)
2. 放在路徑參數(shù)上
示例代碼如下:
@GetMapping("/testParams4/{pathParam}")
public ResponseEntity<String> testParams4(@PathVariable("pathParam") String pathParam) {
return ResponseEntity.ok(MessageFormat.format("pathParam:[{0}];", pathParam));
}
參數(shù)使用
{和}框起來,然后使用@PathVariable即可獲取到值,坑不多。
3. 放在請求頭和Cookie
這兩種情況里面的參數(shù)主要是標識類的參數(shù)如userToken,一般都是不變的,業(yè)務(wù)中很少使用到。
二、接口參數(shù)的類型
1. 數(shù)字、字符串
沒啥坑。
2. 日期
示例代碼如下:
@GetMapping("/testParams5")
public ResponseEntity<String> testParams5(Date date) {
return ResponseEntity.ok(MessageFormat.format("pathParam:[{0}];", date));
}
這里有個問題,這樣的接口前端怎么傳這個
date值,字符串?時間戳?我已經(jīng)替大家試過了,都不行,接口直接報400。
正確的做法是在日期參數(shù)前加上@DateTimeFormat注解,改成這樣就行了:
public ResponseEntity<String> testParams5(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date date)。
傳參的話傳字符串:2023-09-14 00:00:00 即可
image.png
3. 列表
示例代碼如下:
@GetMapping("/testParams6")
public ResponseEntity<String> testParams6(List<Integer> paramList) {
return ResponseEntity.ok(MessageFormat.format("paramList:[{0}];", paramList));
}
這串代碼不用測試,它本身就是錯誤的,前面說過Spring默認使用無參構(gòu)造函數(shù)來實例化對象,但是List是一個接口,沒有無參構(gòu)造函數(shù)。
為了解決這個問題,可以使用Spring的@RequestParam注解來指定參數(shù)名,并將多個參數(shù)值綁定到一個List對象中。
修改代碼如下:
public String testParams6(@RequestParam("paramList") List<Integer> paramList)即可
然后,通過使用逗號分隔的參數(shù)值來訪問接口,如:http://localhost:8080/testParams6?paramList=1,2,3
這樣就可以成功傳遞參數(shù)列表并訪問接口了。
image.png
4. 文件
先寫一個簡單上傳界面
upload.html
<!DOCTYPE html>
<html>
<head>
<title>File Upload Demo</title>
</head>
<body>
<h1>File Upload Demo</h1>
<form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<br/><br/>
<input type="submit" value="Upload" />
</form>
</body>
</html>
后端上傳代碼
FileUploadController.java
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
@Controller
public class FileUploadController {
@PostMapping("/upload")
public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
// Check if file is empty
if (file.isEmpty()) {
return new ResponseEntity<>("File is empty", HttpStatus.BAD_REQUEST);
}
// Save the file
try {
byte[] bytes = file.getBytes();
// Logic to save the file to a desired location
return new ResponseEntity<>("File uploaded successfully", HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>("Failed to upload file", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
上傳接口后端其實還好,主要是前端需要處理的內(nèi)容多一些,由于MultipartFile類也是一個接口,所以這里也需要加上
@RequestParam注解。

三、接口參數(shù)的判斷
前面提到的@RequestBody、@RequestParam注解都是SpringBoot自帶的,它們主要的功能是將請求參數(shù)轉(zhuǎn)換為我們接口定義的變量或者Java對象,而校驗參數(shù)值是否合法通常有下面幾種做法:
- 自己寫校驗邏輯,一般是配合使用Assert進行參數(shù)校驗
- 使用javax.validation包的校驗注解,如
@NotNull、@NotBlank
這里主要講一下javax.validation如何使用!
1. pom.xml引入
<!-- 接口參數(shù)校驗 -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
2. 注解分類
a. 空值檢查
| 注解 | 說明 | 使用頻率 |
|---|---|---|
| @NotNull | 不能為null,常用于數(shù)字、日期 | 常用 |
| @NotBlank | 不能為null也不能為空,常用于字符串 | 常用 |
| @NotEmpty | 集合不能為空,常用于List、Map、Set | 常用 |
b. 數(shù)值檢查
| 注解 | 說明 | 使用頻率 |
|---|---|---|
| @Max | 被注釋的元素必須小于等于指定的值 | 常用 |
| @Min | 被注釋的元素必須大于等于指定的值 | 常用 |
| @Positive | 被注釋的元素必須是正數(shù) | 不常用 |
| @Negative | 被注釋的元素必須是負數(shù) | 不常用 |
c. Boolean 檢查
| 注解 | 說明 | 使用頻率 |
|---|---|---|
| @AssertFalse | 被注釋的元素必須是false | 常用 |
| @AssertTrue | 被注釋的元素必須是true | 常用 |
d. 日期檢查
| 注解 | 說明 | 使用頻率 |
|---|---|---|
| @Future | 被注釋的元素必須是將來的日期 | 不常用 |
| @Past | 被注釋的元素必須是過去的日期 | 不常用 |
e. 日期檢查
| 注解 | 說明 | 使用頻率 |
|---|---|---|
| 被注釋的元素必須是電子郵箱地址 | 常用 | |
| @Pattern | 被注釋的元素必須是符合正則表達式,我經(jīng)常使用這個判斷手機號是否合法 | 常用 |
3. 使用方法
下面是一個經(jīng)典的案例
@Data
public class StudentReq {
@NotBlank(message = "主鍵不能為空")
private String id;
@NotBlank(message = "名字不能為空")
@Size(min = 2, max = 4, message = "名字字符長度必須為 2~4個")
private String name;
@Pattern(regexp = "^1[3456789]\\d{9}$", message = "手機號格式錯誤")
private String phone;
@Email(message = "郵箱格式錯誤")
private String email;
@Past(message = "生日必須早于當前時間")
private Date birth;
@Min(value = 0, message = "年齡必須為 0~100")
@Max(value = 100, message = "年齡必須為 0~100")
private Integer age;
@PositiveOrZero
private Double score;
}
這些東西看看就行了,用的時候翻一下文檔就行,記也記不住。
四、一些可以直接獲取到的參數(shù)
- HttpServletRequest:用于獲取HTTP請求的相關(guān)信息,包括請求頭、請求參數(shù)、請求方法等。
- HttpServletResponse:用于控制HTTP響應(yīng),包括設(shè)置響應(yīng)狀態(tài)碼、設(shè)置響應(yīng)頭、發(fā)送響應(yīng)內(nèi)容等。
- HttpSession:用于獲取當前會話的信息,可以用來存儲和獲取會話級別的數(shù)據(jù)。
- Principal:用于獲取當前用戶的身份信息,通常用于認證和授權(quán)。
- Model/ModelMap:用于在請求處理方法中傳遞數(shù)據(jù)給前端視圖。
- BindingResult:用于獲取請求參數(shù)綁定和驗證的結(jié)果,包含了校驗的錯誤信息。
- Locale:用于獲取當前請求的語言環(huán)境,可以用來進行國際化處理。
- MultipartFile(或者List):用于處理上傳的文件,包括文件的名稱、大小、內(nèi)容等。
- RedirectAttributes:用于在重定向時傳遞數(shù)據(jù)給目標頁面。
- ServletRequest/ServletResponse:HttpServletRequest/HttpServletResponse的父類,可以使用其提供的通用方法。
- @ModelAttribute注解:用于獲取請求參數(shù),并將其綁定到一個對象上。
這些對象可以直接在接口參數(shù)上使用,通過框架自動注入的方式獲取其實例。在使用時,需要保證框架已經(jīng)正確配置和啟用了對應(yīng)的注解和攔截器。用的最多的就是HttpServletRequest和HttpServletResponse了。


