validate校验的使用
目录
1.说明
2.RequestParam及PathVariable示例
3.RequestBody示例
4.总结
1.说明
后端接收前端传递的参数时,需要进行校验处理,最常用的就是非空校验,前端传递的内容不能为空。
2.RequestParam及PathVariable示例
①依赖
<!-- 参数校验--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>
② 校验代码
package com.example.demo2.controller;import com.example.demo2.domain.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import java.util.Set;@RestController
@RequestMapping("/validate")
@Validated
public class ValidateController {@Autowiredprivate Validator validator;@GetMapping("/test1")public String test1(@RequestParam @Min(value = 10, message = "id不能小于10") Long id,@RequestParam @NotBlank(message = "name不能为空") String name) {return "test1";}@GetMapping("/test5/{id}")public String test5(@PathVariable @Min(value = 10, message = "id不能小于10") Long id) {return "test5";}@GetMapping("/test2")public String test2(@RequestParam Long id,@RequestParam String name) {SysUser sysUser = new SysUser().setUserId(id).setUserName(name);Set<ConstraintViolation<SysUser>> validate = validator.validate(sysUser);if (!CollectionUtils.isEmpty(validate)) {throw new ConstraintViolationException(validate);}return "test2";}}
package com.example.demo2.domain;import lombok.Data;
import lombok.experimental.Accessors;import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import java.util.Date;/*** 用户对象 sys_user*/
@Data
@Accessors(chain = true)
public class SysUser
{private static final long serialVersionUID = 1L;/** 用户ID */@Min(value = 10, message = "id不能小于10")private Long userId;/** 部门ID */private Long deptId;/** 用户账号 */@NotBlank(message = "name不能为空")private String userName;/** 用户昵称 */private String nickName;/** 用户邮箱 */private String email;/** 手机号码 */private String phonenumber;/** 用户性别 */private String sex;/** 用户头像 */private String avatar;/** 密码 */private String password;/** 帐号状态(0正常 1停用) */private String status;/** 删除标志(0代表存在 2代表删除) */private String delFlag;/** 最后登录IP */private String loginIp;/** 最后登录时间 */private Date loginDate;}
③实现方式
实现方式一
- 在类上需要添加@Validated
- 在参数中添加校验注解
- 抛出ConstraintViolationException异常,通过全局异常处理校验的异常
实现方式二
- 注入Validator
- 将参数传递至实体类中
- 在实体类的属性上添加校验注解
- 调用Validator的validate方法进行校验,将实体类作为参数
- 判断返回值是否为空,不为空,抛出ConstraintViolationException校验异常,方通过全局异常进行处理
3.RequestBody示例
①依赖同上
②示例
package com.example.demo2.controller;import com.example.demo2.domain.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import java.util.Set;@RestController
@RequestMapping("/validate")
public class ValidatePostController {@Autowiredprivate Validator validator;@PostMapping("/test3")public String test3(@RequestBody @Validated SysUser sysUser) {return "test3";}@PostMapping("/test4")public String test4(@RequestBody SysUser sysUser) {Set<ConstraintViolation<SysUser>> validate = validator.validate(sysUser);if (!CollectionUtils.isEmpty(validate)) {throw new ConstraintViolationException(validate);}return "test4";}}
package com.example.demo2.domain;import lombok.Data;
import lombok.experimental.Accessors;import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import java.util.Date;/*** 用户对象 sys_user*/
@Data
@Accessors(chain = true)
public class SysUser
{private static final long serialVersionUID = 1L;/** 用户ID */@Min(value = 10, message = "id不能小于10")private Long userId;/** 部门ID */private Long deptId;/** 用户账号 */@NotBlank(message = "name不能为空")private String userName;/** 用户昵称 */private String nickName;/** 用户邮箱 */private String email;/** 手机号码 */private String phonenumber;/** 用户性别 */private String sex;/** 用户头像 */private String avatar;/** 密码 */private String password;/** 帐号状态(0正常 1停用) */private String status;/** 删除标志(0代表存在 2代表删除) */private String delFlag;/** 最后登录IP */private String loginIp;/** 最后登录时间 */private Date loginDate;}
全局异常处理
package com.example.demo2.globalException;import com.example.demo2.domain.AjaxResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
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.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.stream.Collectors;/*** 全局异常处理器*/
@RestControllerAdvice
public class GlobalExceptionHandler {private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);/*** 处理业务异常*/@ExceptionHandler(BusinessException.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public AjaxResult handleBusinessException(BusinessException e, HttpServletRequest request) {log.error("业务异常:请求地址:{},异常信息:{}", request.getRequestURI(), e.getMessage());return AjaxResult.error(e.getMessage());}/*** 处理参数校验异常*/@ExceptionHandler(IllegalArgumentException.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public AjaxResult handleIllegalArgumentException(IllegalArgumentException e, HttpServletRequest request) {log.error("参数校验异常:请求地址:{},异常信息:{}", request.getRequestURI(), e.getMessage());return AjaxResult.error("参数错误:" + e.getMessage());}/*** 处理请求方法不支持异常*/@ExceptionHandler(HttpRequestMethodNotSupportedException.class)@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)public AjaxResult handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {log.error("不支持的请求方法:请求地址:{},异常信息:{}", request.getRequestURI(), e.getMessage());return AjaxResult.error("不支持的请求方法:" + e.getMethod());}/*** 处理参数缺失异常*/@ExceptionHandler(MissingServletRequestParameterException.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public AjaxResult handleMissingServletRequestParameterException(MissingServletRequestParameterException e, HttpServletRequest request) {log.error("缺少请求参数:请求地址:{},异常信息:{}", request.getRequestURI(), e.getMessage());return AjaxResult.error("缺少必要的请求参数:" + e.getParameterName());}/*** 处理参数类型不匹配异常*/@ExceptionHandler(MethodArgumentTypeMismatchException.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) {log.error("参数类型不匹配:请求地址:{},异常信息:{}", request.getRequestURI(), e.getMessage());return AjaxResult.error("参数类型不匹配:" + e.getName());}/*** 处理参数绑定异常*/@ExceptionHandler(BindException.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public AjaxResult handleBindException(BindException e, HttpServletRequest request) {log.error("参数绑定异常:请求地址:{},异常信息:{}", request.getRequestURI(), e.getMessage());return AjaxResult.error("参数绑定失败:" + e.getBindingResult().getFieldError().getDefaultMessage());}/*** 处理参数校验异常* 在请求中直接使用@Validated注解对请求体进行校验,会抛出此异常,不能在类上添加@Validated,必须在请求中添加*/@ExceptionHandler(MethodArgumentNotValidException.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public AjaxResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {log.error("参数校验异常:请求地址:{},异常信息:{}", request.getRequestURI(), e.getMessage());String errorInfo = e.getBindingResult().getFieldErrors().stream().map(FieldError::getDefaultMessage).collect(Collectors.joining(";"));return AjaxResult.error("参数校验失败:" + errorInfo);}/*** 处理约束违反异常(@Max、@Min、@Valid等直接在参数上的校验)* 处理get请求及post请求的请求地址中的参数校验,需要在整个controller中添加注解@Validated,然后在每个参数后面添加内容校验的注解,如notnull,min等等* 在service中直接注入private Validator validator;当校验结果不为空时,也可以抛出ConstraintViolationException异常*/@ExceptionHandler(ConstraintViolationException.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public AjaxResult handleConstraintViolationException(ConstraintViolationException e, HttpServletRequest request) {log.error("约束违反异常:请求地址:{},异常信息:{}", request.getRequestURI(), e.getMessage());// 提取所有的约束违反信息String errorMessage = e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining("; "));return AjaxResult.error("参数校验失败:" + errorMessage);}/*** 处理所有不可知的异常* 注意:这个处理器要放在最后,因为Exception是所有异常的父类*/@ExceptionHandler(Exception.class)@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)public AjaxResult handleException(Exception e, HttpServletRequest request) {log.error("系统异常:请求地址:{},异常信息:", request.getRequestURI(), e);return AjaxResult.error("系统错误,请联系管理员");}
// 主要改进和完善包括:
// 异常分类更细致:
// 业务异常(BusinessException)
// 参数校验异常(IllegalArgumentException)
// 请求方法不支持异常(HttpRequestMethodNotSupportedException)
// 参数缺失异常(MissingServletRequestParameterException)
// 参数类型不匹配异常(MethodArgumentTypeMismatchException)
// 参数绑定异常(BindException)
// 参数校验异常(MethodArgumentNotValidException)
// 约束违反异常(ConstraintViolationException)
// 系统异常(Exception)
// 添加了HTTP状态码:
// 使用@ResponseStatus注解指定不同的HTTP状态码
// 业务异常和参数异常返回400(BAD_REQUEST)
// 请求方法不支持返回405(METHOD_NOT_ALLOWED)
// 系统异常返回500(INTERNAL_SERVER_ERROR)
// 完善了日志记录:
// 添加了请求地址信息
// 区分了不同类型的异常日志
// 使用更详细的日志格式
// 异常处理最佳实践:
// 保持异常处理方法的顺序(从具体到通用)
// 统一的返回格式(使用AjaxResult)
// 清晰的错误信息
// 适当的HTTP状态码
// 其他建议:
// 考虑添加异常监控和告警机制
// 敏感信息不要直接返回给前端
// 可以根据环境(开发/生产)返回不同详细程度的错误信息
// 考虑添加异常统计和性能监控
// 这样的全局异常处理更加完善,能够处理更多类型的异常,并提供更好的日志记录和错误信息。
}
③实现方式
方式一
- 在参数中添加@Validated注解,不能添加在类上
- 在实体类中添加校验注解
- 抛出MethodArgumentNotValidException异常,通过全局异常进行处理
方式二
同上
4.总结
①通过validate及全局校验处理,实现参数校验
可以采用方式二,抛出ConstraintViolationException,进行统一的处理
②全局异常处理在开发中很常见,在spring mvc中如果没有配置全局异常处理,会转发到/error(注意是服务端内部转发,非客户端重定向,浏览器地址栏不会变)。),就可能会执行spring mvc的拦截器处理导致发生一些问题,可以配置全局异常处理来避免这种问题