大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。
作为一名从业已达六年的老码农,我的工作主要是开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,我设计过单/多租户体系系统,对接过许多开放平台,也搞过消息中心这类较为复杂的应用,但幸运的是,我至今还没有遇到过线上系统由于代码崩溃导致资损的情况。这其中的原因有三点:一是业务系统本身并不复杂;二是我一直遵循某大厂代码规约,在开发过程中尽可能按规约编写代码;三是经过多年的开发经验积累,我成为了一名熟练工,掌握了一些实用的技巧。
接口参数是导致很多BUG产生的始作俑者,原因在于接口参数有3多:接口参数的取值地方多,如查询参数(Query Parameters)、路径参数(Path Parameters)、请求体(Request Body)等;数据类型多,如数字、字符、日期、文件等;判断情况多,如空值判断、格式判断、大小判断等;
一、接口参数的取值
1. 放在查询参数和请求体里
a、方法参数
示例代码如下:
@GetMapping("/testParams1")
public ResponseEntity<String> testParams1(String param1, Integer param2) {
return ResponseEntity.ok(MessageFormat.format("param1:[{0}];param2:[{1}]", param1, param2));
}
调用请求: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 + '\'' +
'}';
}
}
调用请求:http://localhost:8080/testParams2?param1=111¶m2=222
返回如下:
POST请求
示例代码如下:
@PostMapping("/testParams3")
public ResponseEntity<String> testParams3(ParamsReq paramsReq) {
return ResponseEntity.ok(MessageFormat.format("param1:[{0}];param2:[{1}]", paramsReq.getParam1(), paramsReq.getParam2()));
}
ParamsReq类代码同上
- 调用方式1:参数放在链接上
- 调用方式2:放在Form表单中,content-type为application/x-www-form-urlencoded
- 调用方式3:放在body参数中,content-type为application/json
2. 放在路径参数上
示例代码如下:
@GetMapping("/testParams4/{pathParam}")
public ResponseEntity<String> testParams4(@PathVariable("pathParam") String pathParam) {
return ResponseEntity.ok(MessageFormat.format("pathParam:[{0}];", pathParam));
}
3. 放在请求头和Cookie
二、接口参数的类型
1. 数字、字符串
2. 日期
示例代码如下:
@GetMapping("/testParams5")
public ResponseEntity<String> testParams5(Date date) {
return ResponseEntity.ok(MessageFormat.format("pathParam:[{0}];", date));
}
3. 列表
示例代码如下:
@GetMapping("/testParams6")
public ResponseEntity<String> testParams6(List<Integer> paramList) {
return ResponseEntity.ok(MessageFormat.format("paramList:[{0}];", paramList));
}
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);
}
}
}
三、接口参数的判断
前面提到的@RequestBody
、@RequestParam
注解都是SpringBoot自带的,它们主要的功能是将请求参数转换为我们接口定义的变量或者Java对象,而校验参数值是否合法通常有下面几种做法:
- 自己写校验逻辑,一般是配合使用Assert进行参数校验
- 使用javax.validation包的校验注解,如
@NotNull
、@NotBlank
这里主要讲一下javax.validation如何使用!
1. pom.xml引入
<!-- 接口参数校验 -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
2. 注解分类
a. 空值检查
b. 数值检查
c. Boolean 检查
d. 日期检查
e. 日期检查
3. 使用方法
下面是一个经典的案例
@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;
}
四、一些可以直接获取到的参数
- HttpServletRequest:用于获取HTTP请求的相关信息,包括请求头、请求参数、请求方法等。
- HttpServletResponse:用于控制HTTP响应,包括设置响应状态码、设置响应头、发送响应内容等。
- HttpSession:用于获取当前会话的信息,可以用来存储和获取会话级别的数据。
- Principal:用于获取当前用户的身份信息,通常用于认证和授权。
- Model/ModelMap:用于在请求处理方法中传递数据给前端视图。
- BindingResult:用于获取请求参数绑定和验证的结果,包含了校验的错误信息。
- Locale:用于获取当前请求的语言环境,可以用来进行国际化处理。
- MultipartFile(或者List):用于处理上传的文件,包括文件的名称、大小、内容等。
- RedirectAttributes:用于在重定向时传递数据给目标页面。
- ServletRequest/ServletResponse:HttpServletRequest/HttpServletResponse的父类,可以使用其提供的通用方法。
- @ModelAttribute注解:用于获取请求参数,并将其绑定到一个对象上。
这些对象可以直接在接口参数上使用,通过框架自动注入的方式获取其实例。在使用时,需要保证框架已经正确配置和启用了对应的注解和拦截器。用的最多的就是HttpServletRequest和HttpServletResponse了。