统一参数校验:
在编写Controller层的代码时,时常会有这种情况出现:
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
@PostMapping("register")
public String register(@RequestBody UserDto userDto){
if (StringUtils.isEmpty(userDto.getName())) {
return "用户名不能为空";
}
if(StringUtils.isEmpty(userDto.getPhone())){
return "手机号不能为空";
}
if(userDto.getPhone().length() != 11){
return "手机号格式不正确";
}
if(StringUtils.isEmpty(userDto.getEmail())){
return "邮箱不能为空";
}
userService.register();
return "注册成功";
}
@PutMapping("update")
public String updateInfo(@RequestBody UserDto userDto){
if (StringUtils.isEmpty(userDto.getName())) {
return "用户名不能为空";
}
if(StringUtils.isEmpty(userDto.getPhone())){
return "手机号不能为空";
}
if(userDto.getPhone().length() != 11){
return "手机号格式不正确";
}
if(StringUtils.isEmpty(userDto.getEmail())){
return "邮箱不能为空";
}
userService.updateInfo();
return "修改成功";
}
}
在编写业务层组件实现真正的业务处理逻辑前,需要编写一大堆的参数校验逻辑。controller层可能有多个接口都需要进行参数校验,这样使得开发工作量加大、代码也不简洁。
解决方案:
(1)pom文件中引入spring-boot-starter-validation依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
(2)给实体类字段加上@NotNull注解:
@AllArgsConstructor
@NoArgsConstructor
@Data
public class UserDto {
@NotNull(message = "用户名不能为空")
private String name;
@NotNull(message = "手机号不能为空")
@Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "手机号格式不正确")
private String phone;
@NotNull(message = "邮箱不能为空")
private String email;
private Integer id;
private Integer role;
}
@NotNull:字段不能为null,但可以是空(如"")。
@Pattern:^表示字符串的开头,1表示手机号码的开头数字是1,[3-9]表示第二位数字是3到9之间的数字,\d表示匹配一个数字,{9}表示匹配9个数字,$表示字符串的结尾。不符合正则表达式的手机号都会被视为格式不正确。
(3)在controller层给数据传输对象(xxxDto)加上@Validated注解:
@Resource
private UserService userService;
@PostMapping("register")
public String register(@RequestBody @Validated UserDto userDto,
BindingResult result){
//如果校验不通过,则error对象不为空
//返回错误信息即可
for (ObjectError error : result.getAllErrors()) {
return error.getDefaultMessage();
}
userService.add();
return "注册成功";
}
当客户端调用此接口时,Validator会自动校验数据传输对象UserDto的参数,并将错误信息封装到BingdingResult对象中。getAllErrors()方法会返回一个元素是ObjectError的List集合,如果List集合不为空,说明有参数没有通过校验,返回错误信息即可。
测试:
1、email为空:
我们可以发现,返回的错误信息其实就是我们在使用@NotNull注解时配置的message属性。
2、手机号格式错误:
这里手机号是以数字2开头,而正则表达式规定了手机号只能以数字1开头。
统一结果返回:
controller层返回请求处理结果时,会出现如下场景:
@GetMapping("getString")
public String getString(){
return "hello world";
}
@GetMapping("getInfo")
public UserDto getUserInfo(){
UserDto response = new UserDto(3, "zhangsan", "15798403197", "hutu@163.com", 0);
return response;
}
可以看到有些接口返回String字符串,有些接口返回数据传输对象(xxxDto),接口返回给前端的结果不统一,导致前端不能高效的处理响应数据。
解决方案:
标准的响应对象中应该包含响应状态码(code)、处理结果描述(message)、响应数据(data)等信息。
(1)定义一个ProcessResult枚举类:
public enum ProcessResult {
SUCCESS("3333","操作成功"),
FAIL("9999","操作失败"),
SYSTEM_ERROR("6666","服务正忙,请稍后访问");
private String code;
private String message;
ProcessResult(String code, String message) {
this.code = code;
this.message = message;
}
public String getCode() {
return code;
}
public String getMessage() {
return message;
}
}
(2)定义响应类BaseResponseVO:
@Data
public class BaseResponseVO<T> {
/**
* 响应状态码
* 3333:操作成功
* 6666:系统错误
* 9999:操作失败
*/
private String code;
/**
* 处理结果描述
*/
private String message;
/**
* 响应数据
*/
private T data;
/**
* 成功返回
* @param data:接口需要响应给前端的数据
* @param <T>
* @return
*/
public static <T> BaseResponseVO<T> success(T data){
BaseResponseVO<T> resp = success();
resp.setData(data);
return resp;
}
/**
* 成功返回,但没有返回数据
* @param <T>
* @return
*/
public static <T> BaseResponseVO<T> success(){
BaseResponseVO resp = new BaseResponseVO();
resp.setCode(ProcessResult.SUCCESS.getCode());
resp.setMessage(ProcessResult.SUCCESS.getMessage());
return resp;
}
/**
* 失败返回
* @param code:失败状态码
* @param message:处理结果描述
* @param <T>
* @return
*/
public static <T> BaseResponseVO<T> fail(String code,String message){
BaseResponseVO resp = new BaseResponseVO();
resp.setCode(code);
resp.setMessage(message);
return resp;
}
}
1、枚举类ProcessResult包含了响应状态码、处理结果描述等信息,我们如果需要多个不同的响应码及响应信息,可直接在枚举类中扩展。
2、我们不能确定返回数据的类型,因此使用泛型来声明返回数据。
3、 响应类BaseResponseVO提供了多个静态方法,我们可以根据处理结果调用相应的方法进行返回(成功且有返回数据:success(T data);成功但无返回数据:success();失败:fail(String code,String message))。
(3)修改controller层代码:
@GetMapping("getString")
public BaseResponseVO<String> getString(){
return BaseResponseVO.success("hello world");
}
@GetMapping("getInfo")
public BaseResponseVO<UserDto> getUserInfo(){
UserDto response = new UserDto(3, "zhangsan", "15798403197", "hutu@163.com", 0);
return BaseResponseVO.success(response);
}
测试:
(1)getString接口:
(2)getInfo接口: