目录
1.添加依赖
<!-- 参数校验 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
2.添加拦截器
拦截器用来拦截校验参数统一封装响应数据
package com.hhmt.delivery.handler;
import com.hhmt.delivery.ocpx.bean.ResultVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.stream.Collectors;
/**
* 辉煌明天
* FileName: ValidatedAdvice
* Author: huachun
* email: huachun_w@163.com
* Date: 2022/3/11 11:52
* Description:
*/
@ControllerAdvice
@Slf4j
public class ValidatedAdvice {
//处理Get请求中 使用@Valid 验证路径中请求实体校验失败后抛出的异常,详情继续往下看代码
@ExceptionHandler(BindException.class)
@ResponseBody
public ResultVo BindExceptionHandler(BindException e) {
String message = e.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining());
return ResultVo.error(message);
}
//处理请求参数格式错误 @RequestParam上validate失败后抛出的异常是javax.validation.ConstraintViolationException
@ExceptionHandler(ConstraintViolationException.class)
@ResponseBody
public ResultVo ConstraintViolationExceptionHandler(ConstraintViolationException e) {
String message = e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining());
return ResultVo.error(message);
}
//处理请求参数格式错误 @RequestBody上validate失败后抛出的异常是MethodArgumentNotValidException异常。
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public ResultVo MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
String message = e.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining());
return ResultVo.error(message);
}
}
3.添加拦截配置
如果多个请求参数都校验失败,则遇到第一个校验失败就抛出异常,接下来的异常参数不做校验,配置如下
package com.hhmt.delivery.config;
import org.hibernate.validator.HibernateValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
/**
* 辉煌明天
* FileName: WebValidateConfig
* Author: huachun
* email: huachun_w@163.com
* Date: 2022/3/11 14:27
* Description:
*/
@Configuration
public class WebValidateConfig {
@Bean
public Validator validator() {
ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
.configure()
//failFast的意思只要出现校验失败的情况,就立即结束校验,不再进行后续的校验。
.failFast(true)
.buildValidatorFactory();
return validatorFactory.getValidator();
}
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor();
methodValidationPostProcessor.setValidator(validator());
return methodValidationPostProcessor;
}
}
4.请求参数属性规则定义
package com.hhmt.delivery.entity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
/**
* 辉煌明天
* FileName: BaseMediaDto
* Author: huachun
* email: huachun_w@163.com
* Date: 2022/1/12 11:35
* Description:
*/
@Data
public class BaseMediaDto extends BaseOcpxInfo {
@ApiModelProperty(value = "链路编码,由辉煌明天提供", example = "00000", required = true)
@Length(max = 6, message = "chainCode不能超过6位")
private String chainCode;
@ApiModelProperty(hidden = true)
@Length(max = 256, message = "source不能超过256位")
private String source;
@ApiModelProperty(value = "设备id(imei或idfa的加密值)", example = "d4b8f3898515056278ccf78a7a2cca2d")
private String muid;*/
@Override
public String toString() {
return "BaseMediaDto{" +
"chainCode='" + chainCode + '\'' +
", source='" + source + '\'' +
"} " + super.toString();
}
}
5.添加校验注解
在方法请求处添加 @Validated注解,否则参数不能被校验
package com.hhmt.delivery.controller;
import com.hhmt.delivery.entity.BaseMediaDto;
import com.hhmt.delivery.ocpx.bean.ResultVo;
import com.hhmt.delivery.ocpx.service.common.McService;
import io.swagger.annotations.Api;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
* 辉煌明天
* FileName: MediaCommonController
* Author: huachun
* email: huachun_w@163.com
* Date: 2021/12/29 19:04
* Description:
*/
@Api(tags = "辉煌明天OCPX接口")
@RestController(value = "第三方接入")
@RequestMapping(value = "/cpa", name = "媒体公共调用控制器")
public class OcpxCommonController {
@Autowired
private McService mcService;
@ApiResponses({
@ApiResponse(responseCode = "200", description = "成功"),
@ApiResponse(responseCode = "CPA.00010000", description = "Request exception!"),
@ApiResponse(responseCode = "CPA.00010001", description = "Request parameter exception!"),
})
@Operation(summary = "POST方式OCPX上报")
@PostMapping(value = "/media", name = "媒体上报")
public ResultVo click(@RequestBody @Validated BaseMediaDto mediaDto) {
return mcService.reportCustomer(mediaDto);
}
@Operation(summary = "GET方式OCPX上报")
@GetMapping(value = "/media", name = "媒体上报")
public ResultVo mediaClick(@Validated BaseMediaDto mediaDto) {
return mcService.reportCustomer(mediaDto);
}
@GetMapping(value = "/{chain}/call", name = "客户回传")
public ResultVo call(@RequestParam Map<String, String> map, @PathVariable(value = "chain") String chain) {
return mcService.call(map, chain);
}
}
6.测试参数校验
第一次chainCode超过6位会直接提示
第二次更改chainCode长度提示os参数长度问题
7.注解说明
注解 说明
@AssertFalse 被注释的元素必须为 false
@AssertTrue 被注释的元素必须为 true
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max, min) 被注释的元素的大小必须在指定的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(value) 被注释的元素必须符合指定的正则表达式
8.常见问题
1.只提示异常信息,没有提示具体到参数名称
org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public com.hhmt.delivery.core.domain.model.ResultVo<java.lang.Integer> com.hhmt.delivery.controller.HhChainMediaParamsController.add(com.hhmt.delivery.pojo.entity.HhChainMediaParams): [Field error in object 'hhChainMediaParams' on field 'mediaId': rejected value [null]; codes [NotNull.hhChainMediaParams.mediaId,NotNull.mediaId,NotNull.java.lang.Long,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [hhChainMediaParams.mediaId,mediaId]; arguments []; default message [mediaId]]; default message [ Can't be empty]]
问题原因:服务中有两个地方拦截了校验参数,其中一个处理时候没有处理细节信息
解决办法:删除多余的 MethodArgumentNotValidException 拦截