1、杜绝通篇 if else 参数判断!!!
2、普通参数方式
3、实体对象参数方式
效果
参数不合法,自动使用统一异常返回错误,例如:
{
"code": "-3",
"message": "参数{age}最小18"
}
实现代码
代码一次性给全,主要包含如下几个Java类,拿过去拷贝到项目中就能用:
1、ValidatorController
2、ValidatorVo
3、ExampleValidatorService
4、ResultCode
5、ResultResponseBodyAdvice
6、ResultVO_IGNORE
7、ResultVO
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import link.nuggets.user.module.example.model.ValidatorVo;
import link.nuggets.user.module.example.service.ExampleValidatorService;
/**
* Validator示例
*
* @author 单红宇
* @date 2019年7月8日
*
*/
@Validated // 注意!1) 如果想在参数中使用 @NotNull 这种注解校验,就必须在类上添加 @Validated;2) 如果方法中的参数是对象类型,则必须要在参数对象前面添加 @Validated
@RestController
@RequestMapping("/example/valid")
public class ValidatorController {
@Autowired
private ExampleValidatorService exampleValidatorService;
/**
* 直接参数校验
* 要特别提醒的是,验证框架里面大部分都不需要我们显示设置message,每个注解框架都给了一个默认提示语,大多数提示还都比较友好
*
* @param email
* @return
*/
@GetMapping("/test1")
public String test1(@NotNull(message = "不能为空") @Size(max = 32, min = 6, message = "长度需要在6-32之间") @Email String email) {
return "OK";
}
/**
* 实体类校验
*
* @param validatorVo
* @return
*/
@GetMapping("/test2")
public String test2(@Validated ValidatorVo validatorVo) {
return "Validator OK";
}
/**
* 内部Service校验
*
* @return
*/
@GetMapping("/test3")
public String test3() {
return exampleValidatorService.show("16");
}
}
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import lombok.Data;
/**
* 验证示例VO
*
* @author 单红宇
* @date 2019年7月10日
*
*/
@Data
public class ValidatorVo {
@NotNull(message = "不能为空")
@Size(max = 16, min = 6, message = "字符串长度需要在6-16之间")
private String name;
@Max(value = 100, message = "最大100")
@Min(value = 18, message = "最小18")
private String age;
}
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
/**
* 示例
*
* @author 单红宇
* @date 2019年7月10日
*
*/
@Validated
@Service
public class ExampleValidatorService {
private static final Logger logger = LoggerFactory.getLogger(ExampleValidatorService.class);
public String show(@NotNull(message = "不能为空") @Min(value = 18, message = "最小18") String age) {
logger.info("age = {}", age);
return age;
}
}
/**
* 统一返回值
*
* @author 单红宇
* @date 2019年6月26日
*
*/
public enum ResultCode {
/** 操作成功 */
SUCCESS("1", "成功"),
/** 操作失败 */
FAIL("0", "失败"),
/** 操作失败 */
NULL("-1", "数据不存在"),
/** 系统发生异常 */
EXCEPTION("-2", "系统异常"),
/** 没有权限 */
FORBIDDEN("9403", "没有权限"),
/** 参数错误 */
PARAM_INVALID("-3", "参数错误");
private String code;
private String msg;
private ResultCode(String code, String msg) {
this.code = code;
this.msg = msg;
}
public String code() {
return code;
}
public String msg() {
return msg;
}
}
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* Controller 统一返回包装
*
* @author 单红宇
* @date 2019年6月26日
*
*/
@ControllerAdvice
public class ResultResponseBodyAdvice implements ResponseBodyAdvice<Object> {
private static final Logger logger = LoggerFactory.getLogger(ResultResponseBodyAdvice.class);
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
ServerHttpResponse response) {
// 此处获取到request 为特殊需要的时候处理使用
// HttpServletRequest req = ((ServletServerHttpRequest) serverHttpRequest).getServletRequest();
// 下面处理统一返回结果(统一code、msg、sign 加密等)
if (selectedConverterType == MappingJackson2HttpMessageConverter.class
&& (selectedContentType.equals(MediaType.APPLICATION_JSON)
|| selectedContentType.equals(MediaType.APPLICATION_JSON_UTF8))) {
if (body == null) {
return ResultVO.NULL;
} else if (body instanceof ResultVO) {
return body;
} else {
// 异常
if (returnType.getExecutable().getDeclaringClass().isAssignableFrom(BasicErrorController.class)) {
ResultVO vo = new ResultVO(ResultCode.EXCEPTION);
HttpServletRequest req = ((ServletServerHttpRequest) request).getServletRequest();
if (req.getRequestURL().toString().contains("localhost")
|| req.getRequestURL().toString().contains("127.0.0.1"))
vo.setData(body);
return vo;
} else {
return new ResultVO(body);
}
}
}
return body;
}
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
if (returnType.hasMethodAnnotation(ResultVO_IGNORE.class))
return false;
return true;
}
/**
* Validator 参数校验异常处理
*
* @param ex
* @return
*/
@ExceptionHandler(value = BindException.class)
public ResponseEntity<Object> handleMethodVoArgumentNotValidException(BindException ex) {
FieldError err = ex.getFieldError();
// err.getField() 读取参数字段
// err.getDefaultMessage() 读取验证注解中的message值
String message = "参数{".concat(err.getField()).concat("}").concat(err.getDefaultMessage());
logger.info("{} -> {}", err.getObjectName(), message);
return new ResponseEntity<Object>(new ResultVO(ResultCode.PARAM_INVALID, message), HttpStatus.OK);
}
/**
* Validator 参数校验异常处理
*
* @param ex
* @return
*/
@ExceptionHandler(value = ConstraintViolationException.class)
public ResponseEntity<Object> handleMethodArgumentNotValidException(ConstraintViolationException ex) {
Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
for (ConstraintViolation<?> constraintViolation : constraintViolations) {
PathImpl pathImpl = (PathImpl) constraintViolation.getPropertyPath();
// 读取参数字段,constraintViolation.getMessage() 读取验证注解中的message值
String paramName = pathImpl.getLeafNode().getName();
String message = "参数{".concat(paramName).concat("}").concat(constraintViolation.getMessage());
logger.info("{} -> {} -> {}", constraintViolation.getRootBeanClass().getName(), pathImpl.toString(), message);
return new ResponseEntity<Object>(new ResultVO(ResultCode.PARAM_INVALID, message), HttpStatus.OK);
}
return new ResponseEntity<Object>(new ResultVO(ResultCode.PARAM_INVALID, ex.getMessage()), HttpStatus.OK);
}
}
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Controller默认进行ResultVO包装,对于特殊不需要的,使用该注解可以忽略包装
*
* @author 单红宇
* @date 2019年6月26日
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResultVO_IGNORE {
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
/**
* Controller 统一定义返回类
*
* @author 单红宇
* @date 2019年6月26日
*
*/
public class ResultVO {
private static final Logger logger = LoggerFactory.getLogger(ResultVO.class);
public static final ResultVO SUCCESS = new ResultVO(ResultCode.SUCCESS);
public static final ResultVO FAIL = new ResultVO(ResultCode.FAIL);
public static final ResultVO FORBIDDEN = new ResultVO(ResultCode.FORBIDDEN);
public static final ResultVO NULL = new ResultVO(ResultCode.NULL);
public static final ResultVO EXCEPTION = new ResultVO(ResultCode.EXCEPTION);
public static final ResultVO PARAM_INVALID = new ResultVO(ResultCode.PARAM_INVALID);
/**
* 返回代码
*/
private String code;
/**
* 返回信息
*/
private String message;
/**
* 返回数据
*/
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
/**
* 默认构造,返回操作正确的返回代码和信息
*/
public ResultVO() {
this.setCode(ResultCode.SUCCESS.code());
this.setMessage(ResultCode.SUCCESS.msg());
}
/**
* 构造一个返回特定代码的ResultVO对象
*
* @param code
*/
public ResultVO(ResultCode code) {
this.setCode(code.code());
this.setMessage(code.msg());
}
public ResultVO(String code, String message) {
super();
this.setCode(code);
this.setMessage(message);
}
/**
* 默认值返回,默认返回正确的code和message
*
* @param data
*/
public ResultVO(Object data) {
ResultCode rc = data == null ? ResultCode.NULL : ResultCode.SUCCESS;
this.setCode(rc.code());
this.setMessage(rc.msg());
this.setData(data);
}
/**
* 构造返回代码,以及自定义的错误信息
*
* @param code
* @param message
*/
public ResultVO(ResultCode code, String message) {
this.setCode(code.code());
this.setMessage(message);
}
/**
* 构造自定义的code,message,以及data
*
* @param code
* @param message
* @param data
*/
public ResultVO(ResultCode code, String message, Object data) {
this.setCode(code.code());
this.setMessage(message);
this.setData(data);
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getCode() {
// request请求响应的时候,一定会走到这里,判断如果code不是成功状态,就输出日志
if (!ResultCode.SUCCESS.code().equals(code))
logger.info("ResultVO={}", new Gson().toJson(this));
return code;
}
public void setCode(String code) {
this.code = code;
}
}
一个不需要强调的提示,springboot 的web项目正常都是已经包含如下依赖了,该依赖里已经包含了validation-api
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
validation-api 的注解清单
注释 | 描述 |
---|---|
@AssertFalse | 被注释的元素必须为 false |
@AssertTrue | 同@AssertFalse |
@DecimalMax | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@DecimalMin | 同DecimalMax |
@Digits | 带批注的元素必须是一个在可接受范围内的数字 |
顾名思义 | |
@Future | 将来的日期 |
@FutureOrPresent | 现在或将来 |
@Max | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
@Min | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
@Negative | 带注释的元素必须是一个严格的负数(0为无效值) |
@NegativeOrZero | 带注释的元素必须是一个严格的负数(包含0) |
@NotBlank | 同StringUtils.isNotBlank |
@NotEmpty | 同StringUtils.isNotEmpty |
@NotNull | 不能是Null |
@Null | 元素是Null |
@Past | 被注释的元素必须是一个过去的日期 |
@PastOrPresent | 过去和现在 |
@Pattern | 被注释的元素必须符合指定的正则表达式 |
@Positive | 被注释的元素必须严格的正数(0为无效值) |
@PositiveOrZero | 被注释的元素必须严格的正数(包含0) |
@Szie | 带注释的元素大小必须介于指定边界(包括)之间 |
(END)