Skip to content

参数校验是后端请求的第一道防线,不符合条件的请求,越在前面拦截掉,消耗的资源越少。 对参数进行校验,我们可能会出现如下类似代码:

@RestController@RequestMapping("/user")public classUserControllerextendsBaseController{ @PostMapping("/add") public ApiResultaddUser(@RequestBodyUseruser) { if(user == null) { returnApiResult.fail(" ==对象不能为空==

"); } if(StringUtils.isEmpty(user.getAccount()) || StringUtils.isEmpty(user.getPassword()) || StringUtils.isEmpty(user.getEmail())) { returnApiResult.fail(" ==账号、密码或邮箱不能为空==

"); } if(user.getAccount().length() < 6|| user.getAccount().length() > 11) { returnApiResult.fail(" ==账号长度必须是==6-11==个字符==

"); } if(user.getPassword().length() < 6|| user.getPassword().length() > 16) { returnApiResult.fail(" ==密码长度必须是==6-16==个字符==

"); } if(!Pattern.matches("^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$", user.getEmail())) { returnApiResult.fail(" ==邮箱格式不正确==

"); } // 新增用户操作

returnApiResult.success(); }} ==复制代码== 这样实现并没有什么错,但是看起来实在不够优雅。 下面使用Spring Validator对该代码进行优化。 具体实现Validator + BindResult 首先在对象上通过注解的方式定义校验规则,并指定校验失败后的信息,如下:

package com.minchengjianshe.organizationalandpersonal.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.minchengjianshe.organizationalandpersonal.common.Common; import com.minchengjianshe.organizationalandpersonal.validator.Phone; import lombok.Getter; import lombok.Setter; import javax.validation.constraints.Email; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; @TableName("sys_user") @Getter @Setter public class User extends Common {   @TableId(type = IdType.AUTO)   private Long id;   @NotNull(message = " 用户账号不能为空

")   @Size(min = 6, max = 11, message = " 账号长度必须是6-11个字符

")   private String account;   @NotNull(message = " 用户密码不能为空

")   @Size(min = 6, max = 11, message = " 密码长度必须是6-16个字符

")   private String password;   @Size(max = 40, message = " 用户昵称不能超过40个字符

")   private String nickname;   @Email(message = " 邮箱格式不正确

")   private String email;   @Phone(message = " 手机号格式不正确

")   private String phone; } 校验规则定义完后,在接口上添加@Vaild注解和BindResult参数即可完成校验,如下:

package com.minchengjianshe.organizationalandpersonal.controller; import com.minchengjianshe.organizationalandpersonal.common.ApiResult; import com.minchengjianshe.organizationalandpersonal.entity.User; import org.springframework.validation.BindingResult; import org.springframework.validation.ObjectError; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.validation.Valid; @RestController @RequestMapping("/user") public class UserController extends BaseController {   @PostMapping("/add")   public ApiResult addUser(@RequestBody @Valid User user, BindingResult bindingResult) {     // 参数校验失败,会将错误信息封装成在

BindingResult     for (ObjectError error : bindingResult.getAllErrors()) {       return ApiResult.fail(error.getDefaultMessage());     }     // 新增用户操作

return ApiResult.success();   } } 当我们在访问接口时,未填写用户账户,则会返回如下结果:

{"code":500,"data":null,"message":" ==用户账号不能为空==

"} ==复制代码== 使用该方式已经是非常方便的进行参数校验方式了。 但是不难发现,当有多个接口需要进行参数验证时,就需要在每个接口中添加参数BindingResult。 重复写如下代码: // 参数校验失败,会将错误信息封装成在

BindingResultfor(ObjectError error: bindingResult.getAllErrors()) { returnApiResult.fail(error.getDefaultMessage());} 程序员可不是只会Ctrl+C和Ctrl+V的,我们可以通过异常统一处理来解决这个问题。 Validator + 异常统一处理 如果在接口中不添加参数BindingResult,校验失败则会抛出MethodArgumentNotValidException异常,在异常中包含校验失败的信息。

所以只要统一处理MethodArgumentNotValidException异常即可。 在【异常统一处理的三种方式】这一篇文章中, 介绍了三种异常统一处理的方式,这里使用@ControllerAdvice + @ExceptionHandler的方式

@Slf4j@ControllerAdvicepublic class GlobalExceptionHandler { /** * MethodArgumentNotValidException 异常处理

 */@ResponseBody@ExceptionHandler(MethodArgumentNotValidException.class) public ApiResult methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) { BindingResultbindingResult= e.getBindingResult(); for(ObjectError error : bindingResult.getAllErrors()) { // 返回检验信息

returnApiResult.fail(error.getDefaultMessage()); } returnApiResult.fail(" ==服务异常,请稍后重试==

"); }} 在对异常进行统一处理后,在接口中只需添加注解@Vaild即可,相当的简洁。

package com.minchengjianshe.organizationalandpersonal.controller; import com.minchengjianshe.organizationalandpersonal.common.ApiResult; import com.minchengjianshe.organizationalandpersonal.entity.User; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.validation.Valid; @RestController @RequestMapping("/user") public class UserController extends BaseController {   @PostMapping("/add")   public ApiResult addUser(@RequestBody @Valid User user) {     // 新增用户操作

returnApiResult.success()     return ApiResult.success();   } } 至此,已经能很优雅的使用参数校验。 但是别以为这样就万事大吉了。虽然默认提供的注解能够校验绝大部分的情况,但是还有一些特殊的情况,比如校验用户手机号,这没有现成的Validator。 这时,就需要自定义Validator。 自定义Validator 需要自定义注解和实现校验逻辑。 这里定义注解@Phone,来校验手机号格式:

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})@Retention(RUNTIME)@Documented@Constraint(validatedBy = {PhoneValidator.class})public @interfacePhone { Stringmessage() default" ==手机号格式不正确==

"; Class<?>[]groups() default{}; Class<? extendsPayload>[]payload() default{};} ==复制代码== @Constraint(validatedBy = {PhoneValidator.class}) 用来指定校验类为PhoneValidator。

publicclassPhoneValidatorimplementsConstraintValidator<Phone, String> { @OverridepublicbooleanisValid(Stringphone, ConstraintValidatorContext context) { // 校验手机格式

returnPattern.matches("^1[3-9]\d{9}", phone); }} ==复制代码== 这样就能够通过@Phone来校验手机号格式。

publicclassUser{ @NotNull(message = " ==用户手机号不能为空==

") @Phone(message = " ==手机号格式不正确==

") private String phone;} ==复制代码== 总结 通过Validator和异常统一处理,很优雅的实现了参数校验。并通过自定义Validator,可以实现各种复杂的校验。 感谢阅读,如果感觉有帮助的话,不妨随手点个赞! 源码

github.com/zhuqianchan

… 往期 > 来自

<https://juejin.cn/post/6844904165806718984\>

本站总访问 — 次