SpringBoot参数校验入门

您所在的位置:网站首页 laravel请求参数vo SpringBoot参数校验入门

SpringBoot参数校验入门

2023-06-29 11:48| 来源: 网络整理| 查看: 265

一、添加依赖 org.hibernate.validator hibernate-validator org.projectlombok lombok 二、校验注解分类

1.空和非空检查

@NotBlank:只能用于字符串不为null和"",并且字符串调用trim()方法后的length要大于0。

@NotNull:不能为null。

@Null:必须为null。

@NotEmpty:集合对象元素不能为0,集合不能为空。

2.数值检查

@DecimalMax(value):被注释的元素必须是一个数字,其值必须小于等于指定的最大值。

@DecimalMin(value):被注释的元素必须是一个数字,其值必须大于等于指定的最小值。

@Digits(integer,fraction):被注释的元素必须是一个数字,其值必须在可接受的范围内。

@Positive:被注释的元素必须是正数。

@PositiveOrZero:被注释的元素必须是正数或0。

@Negative:被注释的元素必须是负数。

@NegativeOrZero:被注释的元素必须是负数或0。

@Max(value):被注释的元素的值只能小于或等于该值。

@Min(value):被注释的元素的值只能大于或等于该值。

@Range(min,max):被注释的元素必须在min和max之间。

@Length(min,max): 被注释的字符串的长度必须在min和max之间。

3.Boolean 值检查

@AssertTrue:被注释的元素必须为true。

@AssertFalse:被注释的元素必须为false。

4.长度检查

@Size(min,max):判断字符串、集合、数组、Map的长度是否在min和max之间。

5.日期检查

@Past:被注释的元素的日期必须是过去的日期。

@PathOrPresent:被注释的元素的日期必须是过去或现在的日期。

@Future:被注释的元素的日期必须是将来的日期。

@FutureOrPresent:被注释的元素的日期必须是将来或现在的日期。

6.其他

@URL:判断被注释的字符串必须是一个有效的URL。

@SafeHtml:判断提交的HTML是否安全。

三、@Valid和@Validated的区别

@Valid:可以添加在普通方法、构造方法、方法参数、方法返回、成员变量上,表示它们需要进行约束校验。支持嵌套校验,不支持分组校验。

@Validated:可以添加在类、方法参数、普通方法上,不能用在成员变量上。不支持嵌套校验,支持分组校验。

总的来说,绝大多数场景下,我们使用 @Validated 注解即可。

四、普通测试

1.创建一个用于测试的实体类

import lombok.Data; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Pattern; /** * @author qinxun * @date 2023-06-14 * @Descripion: 用户登录请求类 */ @Data public class UserLoginRequest { /** * 账号 */ @NotBlank(message = "账号不能为空") @Length(min = 2, max = 20, message = "账号的长度为2-20位") @Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号的格式为大小写字母和数字") private String username; /** * 密码 */ @NotBlank(message = "密码不能为空") @Length(min = 4, max = 16, message = "密码的长度为4-16位") private String password; }

2.创建测试控制器

import com.example.quartzdemo.request.UserLoginRequest; import lombok.extern.slf4j.Slf4j; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author qinxun * @date 2023-06-14 * @Descripion: 测试 */ @RestController @RequestMapping("/user") @Slf4j public class UserController { /** * 登录 * * @param userLoginRequest 使用@Validated校验输入项 */ @PostMapping("/login") public String login(@Validated UserLoginRequest userLoginRequest) { log.info("userLoginRequest:{}", userLoginRequest); return "登录验证通过"; } }

3.启动程序,进行测试

我们在postman上进行接口的测试

 没有传入password参数,错误异常提示了密码不能为空。

 我们传入的username不是大小写字母或数字,错误异常日志提示了账号格式为大小写字母或数字。

 我们正确填写了账号和密码,验证通过。

五、分组校验测试

1.我们先创建两个分组接口,一个用于新增一个用于修改。

因为新增和修改的需要的参数不一定相同,比如新增的时候不需要ID,但是修改的时候必须要有ID参数,所以需要使用分组来实现我们的需求。

/** * @author qinxun * @date 2023-06-14 * @Descripion: 新增用户分组 */ public interface AddUserGroup { } /** * @author qinxun * @date 2023-06-14 * @Descripion: 修改用户分组 */ public interface UpdateUserGroup { }

2.修改用户实体,在实体类中加上新增和修改的分组

import com.example.quartzdemo.interfaces.AddUserGroup; import com.example.quartzdemo.interfaces.UpdateUserGroup; import lombok.Data; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; /** * @author qinxun * @date 2023-06-14 * @Descripion: 用户登录请求类 */ @Data public class UserLoginRequest { /** * 用户ID */ @NotNull(message = "用户ID不能为空", groups = UpdateUserGroup.class) private Long id; /** * 账号 */ @NotBlank(message = "账号不能为空", groups = {AddUserGroup.class, UpdateUserGroup.class}) @Length(min = 2, max = 20, message = "账号的长度为2-20位") @Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号的格式为大小写字母和数字") private String username; /** * 密码 */ @NotBlank(message = "密码不能为空", groups = {AddUserGroup.class, UpdateUserGroup.class}) @Length(min = 4, max = 16, message = "密码的长度为4-16位") private String password; }

我们设置ID只能是修改的时候才会判断是否存在,账号和密码在新增和修改的时候都要判断存在。

3.在控制器中测试

import com.example.quartzdemo.interfaces.AddUserGroup; import com.example.quartzdemo.interfaces.UpdateUserGroup; import com.example.quartzdemo.request.UserLoginRequest; import lombok.extern.slf4j.Slf4j; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author qinxun * @date 2023-06-14 * @Descripion: 测试 */ @RestController @RequestMapping("/user") @Slf4j public class UserController { /** * 登录 * * @param userLoginRequest 使用@Validated校验输入项 */ @PostMapping("/login") public String login(@Validated UserLoginRequest userLoginRequest) { log.info("userLoginRequest:{}", userLoginRequest); return "登录验证通过"; } /** * 新增用户 * * @param userLoginRequest 使用@Validated校验输入项 校验的注解中加上了新增分组的参数 */ @PostMapping("/add") public String add(@Validated(AddUserGroup.class) UserLoginRequest userLoginRequest) { log.info("userLoginRequest:{}", userLoginRequest); return "新增用户验证通过"; } /** * 修改用户 * * @param userLoginRequest 使用@Validated校验输入项 校验的注解中加上了修改分组的参数 */ @PostMapping("/update") public String update(@Validated(UpdateUserGroup.class) UserLoginRequest userLoginRequest) { log.info("userLoginRequest:{}", userLoginRequest); return "修改用户验证通过"; } }

我们继续在postman上进行接口的测试

 可以看到我们在新增的时候没有传入ID参数校验通过了。

接下来我们现在调试修改的接口,没有传递ID参数。

 我们在调试修改接口的时候,没有传递ID参数,验证不能通过了。

最后我们加上ID参数,验证通过。

 六、错误提示的友好处理。

1.创建校验不通过的枚举类

/** * @author qinxun * @date 2023-06-14 * @Descripion: 业务层异常枚举 */ public enum ServiceExceptionEnum { SUCCESS(0, "成功"), ERROR(1, "失败"), SYS_ERROR(1000, "服务端发生异常"), MISSING_REQUEST_PARAM_ERROR(1001, "参数缺失"), INVALID_REQUEST_PARAM_ERROR(1002, "请求参数不合法"); private final String message; private final int code; ServiceExceptionEnum(int code, String message) { this.code = code; this.message = message; } public String getMessage() { return message; } public int getCode() { return code; } }

2.统一返回结果实体类

package com.example.quartzdemo.common; import com.example.quartzdemo.enums.ServiceExceptionEnum; import java.io.Serializable; /** * @author qinxun * @date 2023-06-14 * @Descripion: 统一返回结果实体类 */ public class CommonResult implements Serializable { /** * 错误码 */ private Integer code; /** * 错误提示 */ private String message; /** * 返回数据 */ private T data; /** * 成功 * * @param data * @param * @return */ public static CommonResult success(T data) { CommonResult commonResult = new CommonResult(); commonResult.setCode(ServiceExceptionEnum.SUCCESS.getCode()); commonResult.setMessage(ServiceExceptionEnum.SUCCESS.getMessage()); commonResult.setData(data); return commonResult; } /** * 失败 * * @param message * @param * @return */ public static CommonResult error(String message) { CommonResult commonResult = new CommonResult(); commonResult.setCode(ServiceExceptionEnum.ERROR.getCode()); commonResult.setMessage(message); return commonResult; } /** * 失败 * * @param message * @param * @return */ public static CommonResult error(int code, String message) { CommonResult commonResult = new CommonResult(); commonResult.setCode(code); commonResult.setMessage(message); return commonResult; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } @Override public String toString() { return "CommonResult{" + "code=" + code + ", message='" + message + '\'' + ", data=" + data + '}'; } }

3.创建全局异常处理类

package com.example.quartzdemo.exception; import com.example.quartzdemo.common.CommonResult; import com.example.quartzdemo.enums.ServiceExceptionEnum; import lombok.extern.slf4j.Slf4j; import org.springframework.validation.BindException; import org.springframework.validation.ObjectError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.servlet.http.HttpServletRequest; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; /** * @author qinxun * @date 2023-06-14 * @Descripion: 全局异常处理 */ @Slf4j @RestControllerAdvice public class GlobalExceptionHandler { /** * 处理 MissingServletRequestParameterException 异常 *

* SpringMVC 参数不正确 */ @ResponseBody @ExceptionHandler(value = MissingServletRequestParameterException.class) public CommonResult missingServletRequestParameterExceptionHandler(HttpServletRequest req, MissingServletRequestParameterException ex) { log.error("[missingServletRequestParameterExceptionHandler]", ex); // 包装 CommonResult 结果 return CommonResult.error(ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getCode(), ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getMessage()); } @ResponseBody @ExceptionHandler(value = ConstraintViolationException.class) public CommonResult constraintViolationExceptionHandler(HttpServletRequest req, ConstraintViolationException ex) { log.error("[constraintViolationExceptionHandler]", ex); // 拼接错误 StringBuilder detailMessage = new StringBuilder(); for (ConstraintViolation constraintViolation : ex.getConstraintViolations()) { // 使用 ; 分隔多个错误 if (detailMessage.length() > 0) { detailMessage.append(";"); } // 拼接内容到其中 detailMessage.append(constraintViolation.getMessage()); } // 包装 CommonResult 结果 return CommonResult.error( ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + ":" + detailMessage.toString()); } @ResponseBody @ExceptionHandler(value = BindException.class) public CommonResult bindExceptionHandler(HttpServletRequest req, BindException ex) { log.info("========进入了 bindException======"); log.error("[bindExceptionHandler]", ex); // 拼接错误 StringBuilder detailMessage = new StringBuilder(); for (ObjectError objectError : ex.getAllErrors()) { // 使用 ; 分隔多个错误 if (detailMessage.length() > 0) { detailMessage.append(";"); } // 拼接内容到其中 detailMessage.append(objectError.getDefaultMessage()); } // 包装 CommonResult 结果 return CommonResult.error(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getCode(), ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + ":" + detailMessage.toString()); } @ResponseBody @ExceptionHandler(value = MethodArgumentNotValidException.class) public CommonResult MethodArgumentNotValidExceptionHandler(HttpServletRequest req, MethodArgumentNotValidException ex) { log.info("-----------------进入了 MethodArgumentNotValidException-----------------"); log.error("[MethodArgumentNotValidException]", ex); // 拼接错误 StringBuilder detailMessage = new StringBuilder(); for (ObjectError objectError : ex.getBindingResult().getAllErrors()) { // 使用 ; 分隔多个错误 if (detailMessage.length() > 0) { detailMessage.append(";"); } // 拼接内容到其中 detailMessage.append(objectError.getDefaultMessage()); } // 包装 CommonResult 结果 return CommonResult.error(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getCode(), ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + ":" + detailMessage.toString()); } /** * 处理其它 Exception 异常 * * @param req * @param e * @return */ @ExceptionHandler(value = Exception.class) public CommonResult exceptionHandler(HttpServletRequest req, Exception e) { // 记录异常日志 log.error("[exceptionHandler]", e); // 返回 ERROR CommonResult return CommonResult.error(ServiceExceptionEnum.SYS_ERROR.getCode(), ServiceExceptionEnum.SYS_ERROR.getMessage()); } }

我们重新做postman上进行调试

 我们可以看到错误异常的的提示很友好了。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3