网上一些相关资料有些散乱,且有点过时,基于实际工作中的使用情况,做一下总结。
Spring Boot2.7是最后支持JDK8的版本。其spring-boot-starter-web并不内置hibernate-validation,必须先引入spring-boot-starter-validation。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.7.10</version>
</dependency>
配置类
主要是为了配置快速失败,错一个就抛出,这样用不着把所有字段都校验一遍。如果实际工作需要将所有的错误信息都校验并返回,可以忽略。
package com.example.demo.config;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import org.hibernate.validator.HibernateValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ValidatorConfig {
@Bean
public Validator validator() {
ValidatorFactory factory = Validation.byProvider(HibernateValidator.class)
.configure()
.failFast(true)
// 将fail_fast设置为true即可,如果想验证全部,则设置为false或者取消配置即可
//.addProperty("hibernate.validator.fail_fast", "true")//这是另一种写法
.buildValidatorFactory();
return factory.getValidator();
}
}
Controller
在我的实际工作中,这些校验基本都放在Controller中,如果有在Service中进行校验的需求,可以搜索一下资料,实现起来也简单。
之前Controller的写法类似:
@GetMapping("/test")
public ResultInfo<String> test(@Valid UserQueryParam param,BindingResult result) {
if (result.hasErrors()){
Map<String,String> errors = new HashMap<>();
List<FieldError> fieldErrors = result.getFieldErrors();
for (FieldError err : fieldErrors) {
errors.put(err.getField(),err.getDefaultMessage());
}
//返回错误信息
resturn new ResultInfo<String>(400,errors.toString,null);
}
System.out.println(param.toString());
return new ResultInfo<String>(0, "", "ceshi");
}
每个Controller都要进行result.hasErrors()处理,相当不优雅。
我们的Controller在开发规范上做了如下限制:
- 只有GET、POST两个method。
- get请求格式:/getById?userId=1,不使用/user/{id}格式。
- Controller的get参数,<=3个的,用@RequestParam,否则使用参数类。
- POST,使用JSON,用参数类接收。
因此我们的Controller大概写法如下:
package com.example.demo.controller;
import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.params.UserAddParam;
import com.example.demo.params.UserQueryParam;
import com.example.demo.result.ResultInfo;
@RestController
@Validated
public class TestController {
//GET @RequestParam
@GetMapping("/testget")
public ResultInfo<String> testGet(@Min(18) @Max(90) @RequestParam(value = "userId") Long userId) {
System.out.println(userId);
return new ResultInfo<String>(0, "", "ceshi");
}
//GET 参数类
@GetMapping("/testgetpojo")
public ResultInfo<String> testGetPojo(@Valid UserQueryParam param) {
System.out.println(param.toString());
return new ResultInfo<String>(0, "", "ceshi");
}
//POST 参数类
@PostMapping("/testpost")
public ResultInfo<String> testPostPojo(@Valid UserAddParam param) {
System.out.println(param.toString());
return new ResultInfo<String>(0, "", "ceshi");
}
}
POST没什么好讲的。关键在两个GET,请留意Controller类上的@Validated注解,以及两个GET的写法,详情后述。
全局校验错误异常处理类
package com.example.demo.handler;
import javax.validation.ConstraintViolationException;
import org.springframework.validation.BindException;
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 com.example.demo.result.ResultInfo;
@RestControllerAdvice
public class GlobalValidateExceptionHandler {
/**
* POST、GET请求,参数类校验异常
*
* @param ex
* @return
*/
@ExceptionHandler(BindException.class)
@ResponseBody
public ResultInfo<?> handleBindException(BindException ex) {
System.out.println("BindException");
return new ResultInfo<>(400, ex.getBindingResult().getFieldError().getField()
+ ex.getBindingResult().getFieldError().getDefaultMessage(), null);
}
/**
* GET 请求参数校验异常
*
* @param ex
* @return
*/
@ExceptionHandler(ConstraintViolationException.class)
@ResponseBody
public ResultInfo<?> handleGetConstraintViolationException(ConstraintViolationException ex) {
System.out.println("ConstraintViolationException");
return new ResultInfo<>(400, ex.getLocalizedMessage(), null);
}
/**
* GET 请求参数必填校验异常
*
* @param ex
* @return
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseBody
public ResultInfo<?> handleRequestParamException(MissingServletRequestParameterException ex) {
System.out.println("MissingServletRequestParameterException");
return new ResultInfo<>(400, ex.getParameterName()+"不可为空", null);
}
}
基于上面的Controller的写法,对参数的校验有如下:
- GET、POST参数类校验,会抛出BindException异常。
- GET请求参数RequestParam必填项校验,会抛出MissingServletRequestParameterException异常。
- GET请求参数RequestParam的校验(min、max、email、size等),会抛出ConstraintViolationException异常。
在处理类做了统一地处理,并返回相关错误信息,大家可以根据实际工作要求,进行进一步地丰富。