标签:over nal api start 使用 ceo errors 文档 tag
平时在开发接口的时候,经常会需要对参数进行校验,这里提供两种处理校验逻辑的方式。一种是使用Hibernate Validator来处理,另一种是使用全局异常来处理,下面我们讲下这两种方式的用法。
Hibernate Validator是SpringBoot内置的校验框架,只要集成了SpringBoot就自动集成了它,我们可以通过在对象上面使用它提供的注解来完成参数校验。
我们先来了解下常用的注解,对Hibernate Validator所提供的校验功能有个印象。
接下来以添加用户接口的参数校验为例来讲解下Hibernate Validator的使用方法,其中涉及到一些AOP的知识,没有接触过的同学可以去SpringBoot官网查看文档
/**
* 用户传递参数
*/
public class UserParam {
@ApiModelProperty(value = "用户名称",required = true)
@NotEmpty(message = "用户名称不能为空")
private String username;
@ApiModelProperty(value = "密码", required = true)
@NotEmpty
private String password;
@ApiModelProperty(value = "年龄")
@Min(value = 0, message = "年龄最小为0")
private Integer age;
@ApiModelProperty(value = "用户头像",required = true)
@NotEmpty(message = "用户头像不能为空")
private String icon;
//省略Getter和Setter方法...
}
/**
* 用户功能Controller
*/
@Controller
@Api(tags = "UserController", description = "用户管理")
@RequestMapping("/user")
public class UserController {
@Autowired
private UserServiceuserService;
@ApiOperation(value = "添加用户")
@RequestMapping(value = "/create", method = RequestMethod.POST)
@ResponseBody
public CommonResult create(@Validated @RequestBody UserParam user, BindingResult result) {
CommonResult commonResult;
int count = userService.createUser(user);
if (count == 1) {
commonResult = CommonResult.success(count);
} else {
commonResult = CommonResult.failed();
}
return commonResult;
}
}
/**
* HibernateValidator错误结果处理切面
*/
@Aspect
@Component
@Order(2)
public class BindingResultAspect {
@Pointcut("execution(public * com.demo.test.controller.*.*(..))")
public void BindingResult() {
}
@Around("BindingResult()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
if (arg instanceof BindingResult) {
BindingResult result = (BindingResult) arg;
if (result.hasErrors()) {
FieldError fieldError = result.getFieldError();
if(fieldError!=null){
return CommonResult.validateFailed(fieldError.getDefaultMessage());
}else{
return CommonResult.validateFailed();
}
}
}
}
return joinPoint.proceed();
}
}
BindingResult
对象,这样会导致很多重复工作,其实当校验失败时,SpringBoot默认会抛出MethodArgumentNotValidException
或BindException
异常,我们只要全局处理该异常依然可以得到校验失败信息。/**
* 全局异常处理
*/
@ControllerAdvice
public class GlobalExceptionHandler {
@ResponseBody
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public R handleValidException(MethodArgumentNotValidException e) {
BindingResult bindingResult = e.getBindingResult();
String message = null;
if (bindingResult.hasErrors()) {
FieldError fieldError = bindingResult.getFieldError();
if (fieldError != null) {
message = fieldError.getField()+fieldError.getDefaultMessage();
}
}
return CommonResult.failed(message);
}
@ResponseBody
@ExceptionHandler(value = BindException.class)
public R handleValidException(BindException e) {
BindingResult bindingResult = e.getBindingResult();
String message = null;
if (bindingResult.hasErrors()) {
FieldError fieldError = bindingResult.getFieldError();
if (fieldError != null) {
message = fieldError.getField()+fieldError.getDefaultMessage();
}
}
return Response.failed(message);
}
}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
有时候框架提供的校验注解并不能满足我们的需要,此时我们就需要自定义校验注解。比如还是上面的添加品牌,此时有个参数showStatus,我们希望它只能是0或者1,不能是其他数字,此时可以使用自定义注解来实现该功能。
/**
* 用户验证状态是否在指定范围内的注解
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Constraint(validatedBy = FlagValidatorClass.class)
public @interface FlagValidator {
String[] value() default {};
String message() default "flag is not found";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
/**
* 状态标记校验器
*/
public class FlagValidatorClass implements ConstraintValidator<FlagValidator,Integer> {
private String[] values;
@Override
public void initialize(FlagValidator flagValidator) {
this.values = flagValidator.value();
}
@Override
public boolean isValid(Integer value, ConstraintValidatorContext constraintValidatorContext) {
boolean isValid = false;
if(value==null){
//当状态为空时使用默认值
return true;
}
for(int i=0;i<values.length;i++){
if(values[i].equals(String.valueOf(value))){
isValid = true;
break;
}
}
return isValid;
}
}
/**
* 用户传递参数
*/
public class UserParam {
@ApiModelProperty(value = "用户账户状态是否正常")
@FlagValidator(value = {"0","1"}, message = "账户状态不正常,请联系管理员")
private Integer Status;
//省略Getter和Setter方法...
}
这种方式的优点是可以使用注解来实现参数校验,不需要一些重复的校验逻辑,但也有一些缺点,比如需要在Controller的方法中额外注入一个BindingResult对象,只支持一些简单的校验,涉及到要查询数据库的校验就无法满足。
使用全局异常处理来处理校验逻辑的思路很简单,首先我们需要通过@ControllerAdvice注解定义一个全局异常的处理类,然后自定义一个校验异常,当我们在Controller中校验失败时,直接抛出该异常,这样就能达到校验失败返回错误信息的目的。
@ControllerAdvice:类似于@Component注解,可以指定一个组件,这个组件主要用于增强@Controller注解修饰的类的功能,比如说进行全局异常处理。
@ExceptionHandler:用来修饰全局异常处理的方法,可以指定异常的类型。
ApiException
,当我们校验失败时抛出该异常:/**
* 自定义API异常
*/
public class ApiException extends RuntimeException {
private IErrorCode errorCode;
public ApiException(IErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}
public ApiException(String message) {
super(message);
}
public ApiException(Throwable cause) {
super(cause);
}
public ApiException(String message, Throwable cause) {
super(message, cause);
}
public IErrorCode getErrorCode() {
return errorCode;
}
}
/**
* 断言处理类,用于抛出各种API异常
*/
public class Asserts {
public static void fail(String message) {
throw new ApiException(message);
}
public static void fail(IErrorCode errorCode) {
throw new ApiException(errorCode);
}
}
/**
* 全局异常处理
*/
@ControllerAdvice
public class GlobalExceptionHandler {
@ResponseBody
@ExceptionHandler(value = ApiException.class)
public CommonResult handle(ApiException e) {
if (e.getErrorCode() != null) {
return CommonResult.failed(e.getErrorCode());
}
return CommonResult.failed(e.getMessage());
}
}
/**
* 用户管理Controller
*/
@Controller
@Api(tags = "UserController", description = "用户管理")
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
//改进前
@ApiOperation("添加用户")
@RequestMapping(value = "/add/{username}", method = RequestMethod.POST)
@ResponseBody
public CommonResult add(@PathVariable String username) {
return memberCouponService.add(username);
}
//改进后
@ApiOperation("添加用户")
@RequestMapping(value = "/add/{username}", method = RequestMethod.POST)
@ResponseBody
public CommonResult add(@PathVariable String username) {
memberCouponService.add(username);
return CommonResult.success(null,"添加成功");
}
}
/**
* 用户管理Service
*/
public interface UserService {
/**
* 添加用户(改进前)
*/
@Transactional
CommonResult add(String username);
/**
* 添加用户(改进后)
*/
@Transactional
void add(String username);
}
使用全局异常来处理校验逻辑的优点是比较灵活,可以处理复杂的校验逻辑。缺点是我们需要重复编写校验代码,不像使用Hibernate Validator那样只要使用注解就可以了。不过我们可以在上面的Asserts
类中添加一些工具方法来增强它的功能,比如判断是否为空和判断长度等都可以自己实现。
我们可以两种方法一起结合使用,比如简单的参数校验使用Hibernate Validator来实现,而一些涉及到数据库操作的复杂校验使用全局异常处理的方式来实现。
标签:over nal api start 使用 ceo errors 文档 tag
原文地址:https://www.cnblogs.com/jason47/p/14410763.html