springboot gateway post body 为空_SpringBoot参数校验的各种姿势

前言

参数校验是各个项目的基本功能,该文主要介绍如今SpringBoot最常见的JSR参数校验方式与自己在开发中所探索到校验特殊姿势。头条对代码的显示不友好,大家可只关注大致思路逻辑与主要用法,代码可在添加框架依赖时再查看。

传统校验

  • 在controller层进行校验
if(StringUtils.isNotBlank(userVO.getPassword() || ......){// 返回校验错误信息}
  • 在VO里进行参数校验(赋予bean更多行为可以提高代码可读性)
if(!userVO.isCorrectParams()){ // 返回校验错误信息 }

在VO里校验的方法个人是更偏爱的,赋予bean更多行为可以是一个对象的功能与领域行为更清晰,在当今微服务横行的情况下可以简化各层逻辑处理代码,深化对象的业务处理逻辑。即使使用了注解校验,大部分情况下我依旧会把复杂的VO校验逻辑放在VO里处理。

JSR注解简介

JSR是Java Specification Requests的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。以下是Spring对JSR-303版本约束校验工厂的实现,用于生成各种ConstraintValidator(如NotBlankValidator用于校验@NotBlank注解)。

8b233b114bc0a50abf07e125d41df683.png

常用的JSR注解主要为validation-api与hibernat-validator框架下的校验注解,其中hibernat-validator中的注解为validation-api的补充,重叠的注解hibernat-validator已设为过时,所以如果有相同的JSR注解建议优先使用validation-api下的。具体如下图:

f26bfda37ab72488c5fa9f4afe17be4e.png

validation-api注解

b97baf5ddb45b6c15aa82408f3baa88f.png

hibernate-validator注解

JSR注解使用

JSR的注解可以声明的地方十分多,下图以NotBlank为例,目前个人最常用于字段、参数、方法上。当注解用于bean字段或方法上时,需要在bean参数前添加@Valid(validation-api,可用于方法、类、参数、字段)或@Validated(spring,可用于方法、类、参数)注解;当注解用于参数上时,须在参数所在方法或方法所在类上前添加@Valid或@Validated。

9d7487ccadae9b54356764eeb20656aa.png

NotBlank

  • JSR注解字段
@Datapublic class UserVO { @NotBlank(groups = UpdateGroup.class) private String id; private String id; @NotBlank(groups = InsertGroup.class) @ApiModelProperty(value = "用户名") private String username; @NotBlank(groups = UpdateGroup.class) private String password; @Range(min = 1, max = 3, groups = InsertGroup.class) private Integer status;} 

JSR注解设置分组后@Valid/@Validated需要将value设为相应的group才会使用对应group的校验规则,group为一个空接口,用于分组校验,例:

@PutMapping("/")public ResponseEntity update(@RequestBody @Validated(UpdateGroup.class) UserVO vo) { // do something return null;}@PutMapping("/password")public ResponseEntity updatePassword(@RequestBody @Validated ChangePasswordVO vo) { // do something return null;}

当插入时使用InsertGroup的校验规则,UserVO的id必须为空;

当更新时使用UpdateGroup的校验规则,UserVO的id不能为空字符串。

当vo参数校失败时,会抛出MethodArgumentNotValidException异常,此时设置一个全局异常拦截进行该异常捕捉返回即可完成全局body参数校验,例:

 /** * body参数校验错误处理 * * @param exception body格式参数校验 * @return 参数错误 */ @ExceptionHandler({MethodArgumentNotValidException.class}) @ResponseStatus(HttpStatus.BAD_REQUEST) public Object postParamExceptionHandler(MethodArgumentNotValidException exception) { FieldError fieldError = exception.getBindingResult().getFieldError(); return fieldError.getDefaultMessage(); }
JSR注解参数
@RestController@RequestMapping("/user")@Validatedpublic class UserBaseController { @GetMapping("/") public ResponseEntity get(@Min(1) @RequestParam Long id) { // do something return null; }}

在类上添加@Validated后,会自动对类中方法参数上含JSR注解的参数进行校验,body参数需在参数前加,因无法嵌套校验,如果vo中还嵌套了bean属性,则需在bean属性上添加@Valid(@Validated无法用于字段)注解,如:

@Datapublic class UserVO { @Valid private UserDetailVO detailVO;}

当query参数校验失败时会抛出ConstraintViolationException,此处我只对第一个参数异常进行了返回处理,并没有所有异常返回,如下:

 /** * query参数校验错误处理 * * @param exception query格式参数校验 * @return 参数错误 */ @ExceptionHandler({ConstraintViolationException.class}) @ResponseStatus(HttpStatus.BAD_REQUEST) public ResponseEntity getParamExceptionHandler(ConstraintViolationException exception) { ConstraintViolation first = new ArrayList<>(exception.getConstraintViolations()).get(0); String defaultMsg = first.getMessage(); String end = ";"; if (!defaultMsg.endsWith(end)) { return msg(defaultMsg); } defaultMsg = defaultMsg.substring(0, defaultMsg.length() - 1); // 下标0-请求方法(get,post,put,delete),1-校验出错的参数名 String[] methodParam = first.getPropertyPath().toString().split("."); // 编译器可能会把参数编译成arg0、arg1...参数名正常编译则直接返回 if (!methodParam[1].matches(PATTERN_ARG)) { return msg(String.format(messageFormat, methodParam[1], defaultMsg)); } // 参数名为{arg下标}时通过反射获取对应校验出错的参数名称 // 方法的参数数目 int paramSize = first.getExecutableParameters().length; Method method = Arrays.stream(first.getRootBeanClass().getMethods()) .filter(e -> e.getParameters().length == paramSize && methodParam[0].equals(e.getName())) .findFirst() .orElse(null); // 校验出错的参数名,methodParam[0]为校验的方法名 String validationParamName = methodParam[1]; ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); List paramNames = Arrays.asList(parameterNameDiscoverer.getParameterNames(method)); int index = validationParamName.matches(PATTERN_ARG) ? Integer.parseInt(StringUtils.substringAfterLast(validationParamName, "arg")) : paramNames.indexOf(validationParamName); Map map = new HashMap<>(2); map.put(codeKey, HttpStatus.BAD_REQUEST.value()); map.put(msgKey, String.format(messageFormat, paramNames.get(index), defaultMsg)); return new ResponseEntity<>(map, HttpStatus.BAD_REQUEST); }

以下为测试效果图(可以在JSR注解自定义message设置校验失败后的返回信息):

decb82ee29b54ff29e575c6112fbb053.png

上图返回的是中文是因为进行了i18n资源文件语言处理。

JSR注解方法

JSR注解用于方法时,方法需要以is或get为前缀,使用方法同在前文所说的在VO中进行参数校验,例子如下:

@Datapublic class ChangePasswordVO { @NotNull private Long id; @NotBlank private String oldPassword; @NotBlank private String newPassword; @NotBlank private String verifyNewPassword; @AssertTrue(message = "新密码与确认密码不同") @JsonIgnore // 避免作为属性显示到swagger上 public boolean isPasswordEquals() { return Objects.equals(newPassword, verifyNewPassword); }}

为了避免每次都要搭建参数校验轮子,个人把参数校验搭建成简易的SpringBoot全局参数校验异常拦截框架,该文章的拦截器、校验分组、常用VO、各语言校验资源文件都在该框架中,每次项目搭建时只需添加以下依赖即可进行JSR注解异常信息拦截自动返回:

    io.github.wilson-he   web-boot-validation   1.0.2

该框架与个人集成的swagger2-spring-boot-starter(SpringBoot快速自动化配置Swagger)框架结合可加快搭建起一个含基础功能的web项目效率。

觉得文章实用性的强的可关注一下哦,之后会更新个人基于mybatis-plus文件模板集成开发的maven代码生成器,只需添加maven插件依赖进行配置即可一键生成constant-model-dao-service-controller层文件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值