Springboot中使用spring-boot-starter-validation

引言

在日常项目中,偶尔会有后段接口需要对入参进行参数校验的过程,springboot官方提供了对应的校验依赖,现在总结记录一下在使用的过程中会遇到很多坑

在使用此依赖时,我的项目版本为:Static Badge Static Badge

引入依赖

<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-validation</artifactId>  
</dependency>

其中的日志平台可能与当前项目中的日志平台冲突,导致在运行时报错
若报:

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:~/.m2/repository/org/apache/logging/log4j/log4j-slf4j-impl/2.17.0/log4j-slf4j-impl-2.17.0.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:~/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory]
Exception in thread "main" java.lang.ExceptionInInitializerError
	at com.main(App.java:16)
Caused by: org.apache.logging.log4j.LoggingException: log4j-slf4j-impl cannot be present with log4j-to-slf4j
	at org.apache.logging.slf4j.Log4jLoggerFactory.validateContext(Log4jLoggerFactory.java:60)
	at org.apache.logging.slf4j.Log4jLoggerFactory.newLogger(Log4jLoggerFactory.java:44)
	at org.apache.logging.slf4j.Log4jLoggerFactory.newLogger(Log4jLoggerFactory.java:33)
	at org.apache.logging.log4j.spi.AbstractLoggerAdapter.getLogger(AbstractLoggerAdapter.java:53)
	at org.apache.logging.slf4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:33)
	at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:363)
	at org.apache.commons.logging.LogAdapter$Slf4jAdapter.createLocationAwareLog(LogAdapter.java:130)
	at org.apache.commons.logging.LogAdapter.createLog(LogAdapter.java:91)
	at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:67)
	at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:59)
	at org.springframework.boot.SpringApplication.<clinit>(SpringApplication.java:196)
	... 1 more

则剔除

<exclusion>  
    <groupId>ch.qos.logback</groupId>  
    <artifactId>logback-classic</artifactId>  
</exclusion>

若报:

Exception in thread "main" java.lang.ExceptionInInitializerError
	at com.main(App.java:16)
Caused by: org.apache.logging.log4j.LoggingException: log4j-slf4j-impl cannot be present with log4j-to-slf4j
	at org.apache.logging.slf4j.Log4jLoggerFactory.validateContext(Log4jLoggerFactory.java:60)
	at org.apache.logging.slf4j.Log4jLoggerFactory.newLogger(Log4jLoggerFactory.java:44)
	at org.apache.logging.slf4j.Log4jLoggerFactory.newLogger(Log4jLoggerFactory.java:33)
	at org.apache.logging.log4j.spi.AbstractLoggerAdapter.getLogger(AbstractLoggerAdapter.java:53)
	at org.apache.logging.slf4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:33)
	at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:363)
	at org.apache.commons.logging.LogAdapter$Slf4jAdapter.createLocationAwareLog(LogAdapter.java:130)
	at org.apache.commons.logging.LogAdapter.createLog(LogAdapter.java:91)
	at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:67)
	at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:59)
	at org.springframework.boot.SpringApplication.<clinit>(SpringApplication.java:196)
	... 1 more

则剔除

<exclusion>  
    <groupId>org.apache.logging.log4j</groupId>  
    <artifactId>log4j-to-slf4j</artifactId>  
</exclusion>

完整依赖如下:

<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-validation</artifactId>  
    <exclusions>  
        <exclusion>  
            <groupId>ch.qos.logback</groupId>  
            <artifactId>logback-classic</artifactId>  
        </exclusion>  
        <exclusion>  
            <groupId>org.apache.logging.log4j</groupId>  
            <artifactId>log4j-to-slf4j</artifactId>  
        </exclusion>  
    </exclusions>  
</dependency>

使用

对于GET请求

首先在要进行校验的Controller类上添加 org.springframework.validation.annotation@Validated 注解,然后在需要校验的参数上添加对应的校验注解,如@NotNull,@NotEmpty等,例如

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

@Slf4j
@Validated  
@RestController  
@RequestMapping("/validated`")
public class ValidatedController{

	@GetMapping("/get/string")  
	public String validatedGet(@NotEmpty(message = "这里是错误后的校验信息") String param) {  
		return "返回结果";
	}
	
	@GetMapping("/get/integer")  
	public String validatedGet(@NotNull(message = "这里是错误后的校验信息") Integer param) {  
		return "返回结果";
	}

}

localDateTime 的校验比较特殊,因为前端传输的格式大部分都类似于 yyyy-MM-dd HH:mm:ss,后台接受时,因为不符合yyyy-MM-ddTHH:mm:ss,所以导致正确的时间也会校验成错误的选项,因此需要进行额外的参数设置

import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.format.annotation.DateTimeFormat;

import javax.validation.constraints.NotNull;

@Slf4j
@Validated
@RestController
@RequestMapping("/validated`")
public class ValidatedController{

	@GetMapping("/get/time")
	public String validatedGet(@NotNull(message = "这里是错误后的校验信息") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime startTime,  
							  @NotNull(message = "这里是错误后的校验信息") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime endTime) {
      return "返回结果";
	}

}

对于POST请求

post请求不需要在类上添加注解,但是需要在入参中添加 @Validated ,同时将规则添加到对应的实体类中

import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
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;


@Slf4j 
@RestController  
@RequestMapping("/validated`")
public class ValidatedController{

	@PostMapping("/post/eternity")  
	public String validatedGet(@RequestBody @Validated ValidatedVO vo) {
		return "返回结果";
	}

}

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

// 省略get和set
public class ValidatedVO{
  
	@NotEmpty(message = "这里是错误后的校验信息")  
	private String param;

	@NotNull(message="这里是错误后的校验信息")
	private Integer param2;
}

处理报错

添加了校验规则后,如果不符合校验规则,框架会走默认的报错,如果需要自定义报错信息,可以通过@RestControllerAdvice 来实现


import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolationException;

@Slf4j
@RestControllerAdvice
public class ControllerExceptionHandler {

    /**
     * 处理请求参数格式错误 @RequestBody上validate失败后抛出的异常是MethodArgumentNotValidException异常。
     *
     * @param ex 错误信息
     */
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public String handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        BindingResult bindingResult = ex.getBindingResult();
        StringBuilder sb = new StringBuilder("校验失败:");
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", ");
        }
        return sb.toString();
        
    }

    /**
     * 处理请求参数格式错误 @RequestParam上validate失败后抛出的异常是javax.validation.ConstraintViolationException
     * 
     * @param ex 错误信息
     */
    @ExceptionHandler({ConstraintViolationException.class})
    public String handleConstraintViolationException(ConstraintViolationException ex) {
        return ex.getMessage();
    }
}

注意

在使用这个参数校验时,出现了死活也监听不到错误的情况。此处需要注意,要放置在controller这个包的上层,否则会出现扫不到的情况

posted @ 2024-09-23 16:43  忆故人  阅读(979)  评论(0)    收藏  举报