统一参数校验:
在编写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接口:




















