博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Springboot学习06-Spring AOP封装接口自定义校验
阅读量:6133 次
发布时间:2019-06-21

本文共 8466 字,大约阅读时间需要 28 分钟。

Springboot学习06-Spring AOP封装接口自定义校验

关键字

  BindingResult、Spring AOP、自定义注解、自定义异常处理、ConstraintValidator

 

前言

   在实际项目中,对接口的传如的参数需要做校验处理,原来都是在接口里面直接进行if判断,虽然简单,但是每个接口都要重复写,显得冗余;并且返回的数据也无法很好的自定义说明校验情况;如下;

@RequestMapping(value = { "/get/authcode" }, method = {RequestMethod.POST })    public Object getSignInAuthCode(@RequestBody AuthCodeReq authCodeReq) throws Exception {    //每个数据都要这样重复写        if(StringUtils.isBlank(authCodeReq.getMobile())){      //返回数据也是固定格式,无法知道究竟是什么数据校验没通过          return ResponseMessageEnum.ARGUMENT_EXCEPTION.toString();    }     //业务逻辑略    }

 

 

正文

0-封装全局数据校验功能的目的

  1-避免每个接口重复使用if判断,显得冗余

  2-可以自定义返回数据,告诉前端什么数据检验失败

1-POST请求业务逻辑(且数据以json格式放在body中传入)

  1-将数据校验的具体内容放在POJO中,通过注解进行,见源码-01

  2-当前端URL请求(POST请求,且数据以json格式放在body中传入),且有数据校验失败时,传参BindException exception会接收校验失败的结果,见源码-02
  3-使用Spring AOP的前置方法处理数据检验,见源码-03
  3-1-自定义注解MyValidateAopAnnotation(见源码-04),用于定位AOP的Pointcut(见源码-03)
  3-2-AOP前置方法,根据joinPoint获取接口方法BindingResult参数值(见源码-03)
  3-3-如果bindingResult.hasErrors()为true,则表明数据校验没有通过(见源码-03),则直接抛出BindException异常()
  3-4-在GlobalExceptionHandler类中的BindExceptionHandler方法,专门处理BindException异常,返回json数据(见源码-05)

2-GET请求

 

3-源码分析

 

//1-POJOimport org.hibernate.validator.constraints.NotEmpty;import javax.validation.constraints.NotNull;public class ValidateAuthCodeReq {    @NotEmpty(message = "手机号不能为空")//message将在接口返回数据中得以体现    private String mobile;//手机号    @NotEmpty(message = "验证码不能为空")    private String code;//验证码    public String getMobile() {        return mobile;    }    public void setMobile(String mobile) {        this.mobile = mobile;    }    public String getCode() {        return code;    }    public void setCode(String code) {        this.code = code;    }}//2-controller层接口方法@RestController@RequestMapping(value="/api/shop/login")public class ApiShopLoginController extends ApiShopBaseController {    //2-2-校验并绑定手机号    @MyValidateAopAnnotation//自定义注解,用户AOP定位方法    @RequestMapping(value = { "/validate/authcode" }, method = {RequestMethod.POST })    public Object validateAndBind( @Valid  @RequestBody ValidateAuthCodeReq validateAuthCodeReq,BindingResult bindingResult) throws Exception {            //BindingResult封装了数据校验结果            //业务逻辑略    }}//3-AOP方法,统一处理数据校验@Aspect@Componentpublic class ExceptionAspect {    //根据自定义注解MyValidateAopAnnotation定位方法    @Pointcut("@annotation(com.hs.web.common.exception.MyValidateAopAnnotation)")    public void bindPointCut() {    }    @Before("bindPointCut()")    public void before(JoinPoint joinPoint) throws BindException {        // 接收到请求        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();        //获取请求的request        HttpServletRequest request = attributes.getRequest();        //获取BindingResult,并进行判断        Object[] args = joinPoint.getArgs();        BindingResult bindingResult = (BindingResult)args[args.length-1];        if(bindingResult.hasErrors()){            throw  new BindException(bindingResult);        }        System.out.println("args[args.length-1]: "+ args[args.length-1]);        System.out.println("bindingResult" + bindingResult);    }}//4-自定义注解MyValidateAopAnnotation-目的是为了AOP定位@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER })@Retention(RUNTIME)@Documentedpublic @interface MyValidateAopAnnotation {    }//5-全局异常处理类import com.hs.common.util.json.JsonUtil;@ControllerAdvice@ResponseBodypublic class GlobalExceptionHandler {    //数据校验    @ExceptionHandler(value=BindException.class)    public String BindExceptionHandler(HttpServletRequest request,BindException exception) throws Exception{        logger.warn(exception);        //1-封装异常说明        List
errorList = exception.getAllErrors(); StringBuffer sb = new StringBuffer(); for(ObjectError error : errorList){ sb.append("参数" + exception.getFieldError().getField() + "异常:" + exception.getFieldError().getDefaultMessage() + ";"); } //2-封装返回参数 ExceptionResponseBean detailBean = new ExceptionResponseBean( GlobalExceptionEnum.ERROR_DATA_VALIDATION.getCode(), GlobalExceptionEnum.ERROR_DATA_VALIDATION.getMsg(), sb.toString()); //3-以Json格式返回数据 return JsonUtil.toJson(detailBean).toString(); } }//6-异常情况枚举import com.hs.common.util.json.JsonUtil;public enum GlobalExceptionEnum { OTHER_EXCEPTION(800, "出现异常", "其它异常,待识别", Exception.class), ERROR_DATA_VALIDATION(801, "数据校验异常", "请求参数数据校验异常", BindException.class), ; private int code; private String msg; private ExceptionResponseDetailBean data; private Class exception; private GlobalExceptionEnum(int code, String msg, String data, Class exception) { this.code = code; this.msg = msg; this.data = new ExceptionResponseDetailBean(data); this.exception = exception; } public int getCode() { return code; } public String getMsg() { return msg; } public ExceptionResponseDetailBean getData() { return data; } public Class getException() { return exception; }}//7-ExceptionResponseBean异常返回POJOpublic class ExceptionResponseBean { private int code; private String msg; private ExceptionResponseDetailBean data; public ExceptionResponseBean() { super(); } public ExceptionResponseBean(int code, String msg, String detail) { super(); this.code = code; this.msg = msg; this.data = new ExceptionResponseDetailBean(detail); } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public ExceptionResponseDetailBean getData() { return data; } public void setData(ExceptionResponseDetailBean data) { this.data = data; } }//8-ExceptionResponseDetailBean异常返回明细POJOpublic class ExceptionResponseDetailBean { private String detail; public ExceptionResponseDetailBean() { super(); } public ExceptionResponseDetailBean(String detail) { super(); this.detail = detail; } public String getDetail() { return detail; } public void setDetail(String detail) { this.detail = detail; } }

 

 

 

4-应用示例

 

5-进一步优化校验

5-1-优点和问题

  • 上面的数据校验已经进行分装,实现了和接口业务低耦合要求,并且可以自定义结果;但有一个问题:对于每一个POJO要校验的参数,都要重复指定message值,
  • 示例:@NotEmpty(message = "手机号不能为空") ;如果多个POJO对手机号验证,又会出现冗余情况

5-2-优化思路

  • 自定义校验注解,对相同或类似的参数使用相同的自定义注解(见源码-01)
  • 自定义注解需要,自定义一个注解类(见源码-03)和一个ConstraintValidator实现类(见源码-03)

5-3-源码分析

//1-POJOpackage com.hs.api.shopapp.entity.commom;import com.hs.web.common.annotation.validation.MobileFormat;public class AuthCodeReq {    @MobileFormat//使用自定义校验注解    private String mobile;//手机号    public String getMobile() {        return mobile;    }    public void setMobile(String mobile) {        this.mobile = mobile;    }}//2-自定义注解MobileFormat@Documented@Target({ElementType.METHOD, ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)@Constraint(validatedBy = MobileConstraintValidator.class)public @interface MobileFormat {    String message() default "手机号格式不正确";    Class
[] groups() default {}; Class
[] payload() default {}; @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface List { NotBlank[] value(); }}//3-重写ConstraintValidator接口,自定义校验规则public class MobileConstraintValidator implements ConstraintValidator
{ @Override public void initialize(MobileFormat constraintAnnotation) { } //在当前方法指定校验规则 @Override public boolean isValid(String value, ConstraintValidatorContext context) { if(StringUtil.isBlank(value)){ return false; } return true; }}//4-测试接口@RestController@RequestMapping(value="/api/shop/login")public class ApiShopLoginController extends ApiShopBaseController { @MyValidateAopAnnotation @RequestMapping(value = { "/get/authcode" }, method = {RequestMethod.POST }) public Object getSignInAuthCode(@Valid @RequestBody AuthCodeReq authCodeReq,BindingResult bindingResult) throws Exception { //业务逻辑略 }}

5-4-应用示例

 

6-备注

6-1-@NotEmpty、@NotNull、@NotBlank 的区别

  • @NotEmpty 用在集合上面(不能注释枚举)
  • @NotBlank用在String上面
  • @NotNull用在所有类型上面

 

参考文献

1-

2-

转载于:https://www.cnblogs.com/wobuchifanqie/p/10181785.html

你可能感兴趣的文章
10、程序员和编译器之间的关系
查看>>
前端学习之正则表达式
查看>>
配置 RAILS FOR JRUBY1.7.4
查看>>
AndroidStudio中导入SlidingMenu报错解决方案
查看>>
修改GRUB2背景图片
查看>>
Ajax异步
查看>>
好记性不如烂笔杆-android学习笔记<十六> switcher和gallery
查看>>
JAVA GC
查看>>
codeforce 599B Spongebob and Joke
查看>>
3springboot:springboot配置文件(外部配置加载顺序、自动配置原理,@Conditional)
查看>>
9、Dubbo-配置(4)
查看>>
前端第七天
查看>>
BZOJ 2190[SDOI2008]仪仗队
查看>>
图解SSH原理及两种登录方法
查看>>
[转载] 七龙珠第一部——第058话 魔境圣地
查看>>
【总结整理】JQuery基础学习---样式篇
查看>>
查询个人站点的文章、分类和标签查询
查看>>
基础知识:数字、字符串、列表 的类型及内置方法
查看>>
JSP的隐式对象
查看>>
JS图片跟着鼠标跑效果
查看>>