因为前端传参的时候有可能少传值或者传值不规范,比如时间等参数,参数在代码里面写校验比较麻烦,所以为了解决这种情况,推荐使用参数校验@Valid。
依赖要引入,否则会出现注解不生效的情况- controller必须使用@Valid
注解在实体类上,否则不生效GET请求不支持此种方法校验参数,但是可以自己写校验注解在GET请求上实体类嵌套实体类或者实体类嵌套数组必须加 @Valid,否则嵌套中的验证不生效- @Future、@Past 必须是将来时间、过去时间,并且类型为
java.util.Date- 我把@Future的类型改成字符串则抛出了
HV000030: No validator could be found for constraint 'javax.validation.constraints.Future' validating type 'java.lang.String'. Check configuration for 'toTime的异常,因为@Future是且类型必须为java.util.Date,所以说明是注解使用方法不正确。
<!--参数校验依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.Valid;
import javax.validation.constraints.*;
import java.util.Date;
/**
* 用户信息
*
* @author lenovo
* @date 2023/9/14
*/
@Data
public class Users {
/**
* id
*/
private String id;
/**
* 姓名
*/
@Length(min = 6,max = 18,message = "姓名长度限制6-18")
private String name;
/**
* 密码
*/
@NotNull(message = "密码不能为空")
private String password;
/**
* 邮件
*/
@Email(message = "邮件格式不正确")
private String email;
/**
* 手机号
*/
@NotNull(message = "手机号不能为空")
private String phone;
/**
* 年龄
*/
@Max(value = 35,message = "年龄不能超过35岁")
private Integer age;
/**
* 退休年龄
*/
@Min(value = 55,message = "退休年龄不能低于55岁")
private Integer outAge;
/**
* 预计到达时间(必须是将来时间)
*/
@Future(message = "必须是将来时间,并且类型为java.util.Date")
private Date toTime;
/**
* 出生时间(必须是过去时间)
*/
@Past(message = "必须是过去时间,并且类型为java.util.Date")
private Date birthDay;
/**
* 用户地址
* 嵌套必须加 @Valid,否则嵌套中的验证不生效
*/
@Valid
@NotNull(message = "用户地址不能为空")
private Address address;
}
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* 用户地址
* @author lenovo
* @date 2023/9/14
*/
@Data
public class Address {
/**
* 用户id
*/
@NotNull(message = "用户id不为空")
private String userId;
/**
* 省
*/
@NotNull(message = "省不为空")
private String province;
/**
* 市
*/
@NotNull(message = "市不为空")
private String city;
/**
* 县
*/
@NotNull(message = "县不为空")
private String county;
}
/**
* 注册用户
* @param users
* @return
*/
@PostMapping("/register")
public R register(@Valid @RequestBody Users users) {
System.out.println("接口正常");
return R.ok(users);
}
什么是全局异常?
全局异常是指一种机制,可以捕获应用程序中的所有异常,无论是来自系统层面还是应用程序层面的异常,都可以被统一处理。一旦异常被捕获,就可以根据不同的情况进行相应的处理,比如记录日志、显示友好的错误信息、发送邮件通知等等。
在Web开发中,常见的全局异常处理器是通过在应用程序中设置一个异常拦截器来实现的。当应用程序中发生异常时,该拦截器就会被触发,捕获异常并进行相应处理。这样可以保证应用程序的健壮性和可靠性,提高用户体验和数据安全性。
import com.lottery.common.R;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.MyBatisSystemException;
import org.springframework.data.mapping.MappingException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
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.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.UnexpectedTypeException;
import java.nio.file.AccessDeniedException;
import java.sql.SQLException;
import java.util.List;
/**
* @author xu
* 全局异常处理器
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandlerResolver {
/**
* 全局异常
*/
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R handleGlobalException(Exception e) {
log.error("全局异常信息 ex={}", e.getMessage(), e);
return R.failed("网络异常-0");
}
/**
* 拒绝访问的异常
*/
@ExceptionHandler(AccessDeniedException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
public R handleAccessDeniedException(AccessDeniedException e) {
log.error("拒绝授权异常信息 ex={}", "拒绝授权", e);
return R.failed(e.getLocalizedMessage());
}
/**
* 方法参数无效异常
*
* @param e
* @return R
*/
@ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R handleBodyValidException(MethodArgumentNotValidException e) {
// 获取异常信息
BindingResult exceptions = e.getBindingResult();
// 判断异常中是否有错误信息,如果存在就使用异常中的消息,否则使用默认消息
if (exceptions.hasErrors()) {
List<ObjectError> errors = exceptions.getAllErrors();
if (!errors.isEmpty()) {
// 这里列出了全部错误参数,按正常逻辑,只需要第一条错误即可
FieldError fieldError = (FieldError) errors.get(0);
return R.failed(fieldError.getDefaultMessage());
}
}
return R.failed("参数异常");
}
/**
* 处理空指针的异常
*
* @param e
* @return
*/
@ExceptionHandler(value = NullPointerException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R exceptionHandler(NullPointerException e) {
log.error("发生空指针异常!原因是 ex={}", e.getMessage(), e);
return R.failed("网络异常-1");
}
/**
* SQL的异常
*
* @param e
* @return
*/
@ExceptionHandler(value = SQLException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R exceptionHandler(SQLException e) {
log.error("发生SQL异常!原因是 ex={}", e.getMessage(), e);
return R.failed("网络异常-2");
}
/**
* 字符串越界的异常
*
* @param e
* @return
*/
@ExceptionHandler(value = ArrayIndexOutOfBoundsException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R exceptionHandler(ArrayIndexOutOfBoundsException e) {
log.error("发生字符串越界异常!原因是 ex={}", e.getMessage(), e);
return R.failed("网络异常-3");
}
/**
* 元素为负数的数组的异常
*
* @param e
* @return
*/
@ExceptionHandler(value = ArrayStoreException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R exceptionHandler(ArrayStoreException e) {
log.error("发生元素为负数的数组异常!原因是 ex={}", e.getMessage(), e);
return R.failed("网络异常-4");
}
/**
* 强制类型转换异常
*
* @param e
* @return
*/
@ExceptionHandler(value = ClassCastException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R exceptionHandler(ClassCastException e) {
log.error("发生强制类型转换异常(将对象强制转换为不是它实际类型的类型)!原因是 ex={}", e.getMessage(), e);
return R.failed("网络异常-5");
}
/**
* 强制类型转换异常
*
* @param e
* @return
*/
@ExceptionHandler(value = NumberFormatException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R exceptionHandler(NumberFormatException e) {
log.error("发生强制类型转换错误异常(将一个字符串转换为数值类型时,字符串不是一个合法的数值格式)!原因是 ex={}", e.getMessage(), e);
return R.failed("网络异常-6");
}
/**
* getOne()查询多条数据异常
*
* @param e
* @return
*/
@ExceptionHandler(value = MyBatisSystemException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R exceptionHandler(MyBatisSystemException e) {
log.error("发生getOne()查询多条数据!原因是 ex={}", e.getMessage(), e);
return R.failed("网络异常-7");
}
/**
* mapper映射到实体时出现异常
*
* @param e
* @return
*/
@ExceptionHandler(value = MappingException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R exceptionHandler(MappingException e) {
log.error("mapper映射到实体时出现异常!原因是 ex={}", e.getMessage(), e);
return R.failed("网络异常-8");
}
/**
* 忽略参数异常处理器
*
* @param e 忽略参数异常
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R parameterMissingExceptionHandler(MissingServletRequestParameterException e) {
log.error("忽略参数异常处理器!原因是 ex={}", e.getMessage(), e);
return R.failed("请求参数 " + e.getParameterName() + " 不能为空");
}
/**
* 缺少请求体异常处理器
*/
@ExceptionHandler(HttpMessageNotReadableException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R parameterBodyMissingExceptionHandler(HttpMessageNotReadableException e) {
log.error("缺少请求体异常处理器:原因 ex={}", e.getMessage(), e);
return R.failed("参数体不能为空");
}
/**
* 参数异常处理器
*/
@ExceptionHandler(UnexpectedTypeException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public R parameterBodyMissingExceptionHandler(UnexpectedTypeException e) {
log.error("缺少请求体异常处理器:原因 ex={}", e.getMessage(), e);
return R.failed(e.getMessage());
}
}