SpringBoot数据校验+全局异常处理--@RequestBody与@Valid一起使用校验失效

场景:

SpringBoot 通过 spring-boot-starter-validation 模块包含了数据校验的⼯作,只需要简单几个注解就可以对请求数据进行校验,很方便。这过程还包含了全局异常的处理,以下是步骤:

1、User 实体类属性添加校验注解

public class User{
 
 private Integer id;
 //校验注解
 @NotBlank(message = "⽤户名不能为空!")
 private String userName;
 @NotBlank(message = "⽤户密码不能为空!")
 @Length(min = 6, max = 10,message = "密码⻓度⾄少6位但不超过10位!")
 private String userPwd;
 
 /*
 省略get set ⽅法 
 */
}

2、在控制层的接口方法形参上添加@RequestBody+@Valid 注解

@RestController
public class UserController {
    @Autowired
    private UserService userService;
/**
     * 添加用户
     * @param user
     * @return
     */
@PostMapping("user")
    public ResultInfo addUser(@RequestBody @Valid User user)
    {

        //结果集
        ResultInfo resultInfo = new ResultInfo();
        //有了全局异常处理GlobalExceptionHandler类,可以不用加try catch
        userService.addUser(user);

        //没有异常则返回默认的结果值:200,添加成功
        return resultInfo;
    }
   }

3、在全局异常处理类中添加校验异常方法BindExceptionHandler

/**
 * 全局异常处理
 * 返回JSON格式
 */

//Controller增强器,可对controller中被 @RequestMapping注解的方法加一些逻辑处理,当将异常抛到controller时,可以对异常进行统一处理,规定返回的json格式或是跳转到一个错误页面
@ControllerAdvice
public class GlobalExceptionHandler {
/**
     * 数据校验全局处理
     * BindException是@Valid使用校验失败时产生的异常
     */
    @ExceptionHandler(value = BindException.class)
    @ResponseBody
    public ResultInfo BindExceptionHandler(BindException e)
    {

        //捕获数据校验异常
        ResultInfo resultInfo = new ResultInfo();
        resultInfo.setCode(500);
        //获取实体类定义的校验注解字段上的message作为异常信息,@NotBlank(message = "用户密码不能为空!")异常信息即为"用户密码不能为空!"
        resultInfo.setResultmsg(e.getBindingResult().getFieldError().getDefaultMessage());
        return resultInfo;
    }
     /**
     * 全局异常处理
     *如果设置了特定异常处理,全局异常处理可作为兜底异常
     * @param e
     * @return
     */
    //设置处理的异常类型,如果没有特定异常则为最大的异常Exception.class
    @ExceptionHandler(value = Exception.class)
    //最终结果是JSON格式
    @ResponseBody
    public ResultInfo exceptionHandler(Exception e)
    {

        ResultInfo resultInfo = new ResultInfo();
        //一旦进入这个方法说明有异常,这是固定的异常信息
        resultInfo.setCode(100);
        resultInfo.setResultmsg("操作异常!");
        return resultInfo;
    }
}

问题描述

使用postman测试结果,userPwd密码故意设置成不符合规范的密码,但是测试捕获异常的不是校验异常而是全局异常
在这里插入图片描述
而在控制台中却把校验异常的错误打印出来了

2022-03-13 13:27:26.701  WARN 14232 --- [nio-8080-exec-1] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.xxxx.springboot_mybatis.po.vo.ResultInfo com.xxxx.springboot_mybatis.controller.UserController.addUser(com.xxxx.springboot_mybatis.po.User): 
[Field error in object 'user' on field 'userPwd': rejected value [222]; codes [Length.user.userPwd,Length.userPwd,Length.java.lang.String,Length]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.userPwd,userPwd]; arguments []; default message [userPwd],10,6]; default message [密码⻓度⾄少6位但不超过10!]] ]

我又单独使用了注解@Valid而没有使用@RequestBody

@RestController
public class UserController {
    @Autowired
    private UserService userService;
/**
     * 添加用户
     * @param user
     * @return
     */
@PostMapping("user")
    public ResultInfo addUser(@Valid User user)
    {

        //结果集
        ResultInfo resultInfo = new ResultInfo();
        //有了全局异常处理GlobalExceptionHandler类,可以不用加try catch
        userService.addUser(user);

        //没有异常则返回默认的结果值:200,添加成功
        return resultInfo;
    }
   }

还是同样的测试数据,这次的测试结果如下:
数据校验异常捕获成功
在这里插入图片描述
测试结果和控制台打印异常一致

2022-03-13 13:40:31.560  WARN 11844 --- [nio-8080-exec-5] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors

Field error in object 'user' on field 'userPwd': rejected value [222]; codes [Length.user.userPwd,Length.userPwd,Length.java.lang.String,Length]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.userPwd,userPwd]; arguments []; default message [userPwd],10,6]; default message [密码⻓度⾄少6位但不超过10!]]

原因分析:

对比一下两次的测试结果以及控制台打印异常,发现了一个不同的地方,控制台显示使用@RequestBody+@Valid注解产生的异常是MethodArgumentNotValidException,而单独使用注解@Valid产生的异常是BindException。


解决方案:

提示:在全局异常处理类中再添加一个新的方法,用于捕获控制台打印的异常MethodArgumentNotValidException

/**
     * 数据校验全局处理
     * MethodArgumentNotValidException是@RequestBody和@Validated配合时产生的异常,比如在传参时如果前端的json数据里部分缺失@RequestBody修饰的实体类的属性就会产生这个异常。
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseBody
    public ResultInfo MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e)
    {

        //捕获数据校验异常
        ResultInfo resultInfo = new ResultInfo();
        resultInfo.setCode(600);
        //获取实体类定义的校验注解字段上的message作为异常信息,@NotBlank(message = "用户密码不能为空!")异常信息即为"用户密码不能为空!"
        resultInfo.setResultmsg(e.getBindingResult().getFieldError().getDefaultMessage());
        return resultInfo;
    }

在控制层的接口方法形参上添加@RequestBody+@Valid 注解再测试一遍,数据校验异常捕获成功
在这里插入图片描述
控制台同样打印出了MethodArgumentNotValidException异常

2022-03-13 13:53:42.574  WARN 11732 --- [nio-8080-exec-1] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.xxxx.springboot_mybatis.po.vo.ResultInfo com.xxxx.springboot_mybatis.controller.UserController.addUser(com.xxxx.springboot_mybatis.po.User): 
[Field error in object 'user' on field 'userPwd': rejected value [222]; codes [Length.user.userPwd,Length.userPwd,Length.java.lang.String,Length]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.userPwd,userPwd]; arguments []; default message [userPwd],10,6]; default message [密码⻓度⾄少6位但不超过10!]] ]

完结撒花~~~

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
A:好的,下面是一个使用springboot、mybatis、swagger2、valid参数校验、图片上传、异常统一处理、返回体封装、vue构建相册项目的CRUD接口实现的代码示例。 1. 定义实体类 ```java public class Album { private Long id; @NotBlank(message = "相册名称不能为空") private String name; @NotBlank(message = "相册描述不能为空") private String description; private String coverUrl; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date createTime; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private Date updateTime; // getter和setter省略... } ``` 2. 定义Mapper接口 ```java @Mapper public interface AlbumMapper { List<Album> findAll(); Album findById(Long id); int insert(Album album); int update(Album album); int deleteById(Long id); } ``` 3. 定义Service接口及实现类 ```java public interface AlbumService { List<Album> findAll(); Album findById(Long id); void save(Album album); void update(Album album); void deleteById(Long id); } @Service public class AlbumServiceImpl implements AlbumService { @Autowired private AlbumMapper albumMapper; @Override public List<Album> findAll() { return albumMapper.findAll(); } @Override public Album findById(Long id) { return albumMapper.findById(id); } @Override public void save(@Valid Album album) { album.setCreateTime(new Date()); album.setUpdateTime(new Date()); albumMapper.insert(album); } @Override public void update(@Valid Album album) { album.setUpdateTime(new Date()); albumMapper.update(album); } @Override public void deleteById(Long id) { albumMapper.deleteById(id); } } ``` 4. 定义Controller接口 ```java @RestController @RequestMapping("/album") @Api(tags = "相册管理接口") public class AlbumController { @Autowired private AlbumService albumService; @GetMapping("/") @ApiOperation("查询所有相册") public Result findAll() { List<Album> albumList = albumService.findAll(); return Result.success(albumList); } @GetMapping("/{id}") @ApiOperation("查询相册详情") public Result findById(@PathVariable Long id) { Album album = albumService.findById(id); if (album != null) { return Result.success(album); } else { return Result.error(ResultCode.NOT_FOUND); } } @PostMapping("/") @ApiOperation("添加相册") public Result save(@Valid @RequestBody Album album) { albumService.save(album); return Result.success(); } @PutMapping("/{id}") @ApiOperation("更新相册") public Result update(@PathVariable Long id, @Valid @RequestBody Album album) { Album old = albumService.findById(id); if (old != null) { album.setId(id); albumService.update(album); return Result.success(); } else { return Result.error(ResultCode.NOT_FOUND); } } @DeleteMapping("/{id}") @ApiOperation("删除相册") public Result deleteById(@PathVariable Long id) { Album album = albumService.findById(id); if (album != null) { albumService.deleteById(id); return Result.success(); } else { return Result.error(ResultCode.NOT_FOUND); } } } ``` 5. 定义全局异常处理器 ```java @ControllerAdvice @ResponseBody public class GlobalExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException.class) public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { FieldError fieldError = e.getBindingResult().getFieldError(); String message = fieldError.getDefaultMessage(); return Result.error(ResultCode.INVALID_PARAMETER, message); } // other exception handler... } ``` 6. 完成以上步骤后,启动SpringBoot应用程序,然后使用SwaggerUI测试接口。同时,在前端项目中可以使用Vue框架实现前端展示及交互。 以上就是使用springboot、mybatis、swagger2、valid参数校验、图片上传、异常统一处理、返回体封装、vue构建相册项目的CRUD接口实现的一个简单示例,仅供参考。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lynn Lu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值