• 为spring的controller做一个打印日志、验证参数、提供默认异常处理的切面


    大纲:

    1. 需求
    2. 实现
    3. 使用

    一、需求

     使用spring的controller时候,有很多重复性操作,可以做一个业务轮子统一实现这些功能。

    1.打印日志:调用接口所属的类、方法名称、接口入参、出参、异常、接口调用时间等信息,出入参对象需要重写toString方法
    2.请求参数校验,无需使用@valid注解,支持分组校验
    3.提供系统业务异常处理,其他异常默认响应500,服务器异常。

    二、实现

    首先定义一个用于方法注解,用于controller的方法上

    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface RestRequestHelper {
        Class[] validGroups() default {}; //参数校验的分组
    }

    然后定义切面around这个自定义的注解

    代码中Result(响应结果)、BaseException(自定义业务异常)都是系统内自己定义的,根据业务需求设计即可。

    @Aspect
    @Component
    @Slf4j
    public class RequestHelperAspect {
    
        @Autowired
        private SmartValidator validator;
    
        @Around("@annotation(restRequestHelper)")
        public Object process(ProceedingJoinPoint pjp, RestRequestHelper restRequestHelper) {
            Class<?> targetClass = pjp.getTarget().getClass();
            String className = targetClass.getSimpleName();
            String methodName = pjp.getSignature().getName();
            Object[] args = pjp.getArgs();
            Object[] arguments = new Object[args.length];
            for (int i = 0; i < args.length; i++) {
                if (args[i] instanceof ServletRequest || args[i] instanceof ServletResponse || args[i] instanceof MultipartFile) {
                    //ServletRequest不能序列化,从入参里排除,否则报异常:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
                    //ServletResponse不能序列化 从入参里排除,否则报异常:java.lang.IllegalStateException: getOutputStream() has already been called for this response
                    continue;
                }
                arguments[i] = args[i];
            }
            log.info("processing request:{}.{} begin,params:{}", className, methodName, arguments);
            long startTime = System.currentTimeMillis();
            Object proceed = null;
            if ((proceed = validParams(arguments,restRequestHelper.validGroups())) == null) {
                try {
                    proceed = pjp.proceed();
                } catch (Throwable throwable) {
                    log.error("processing request error:", throwable);
                    //异常处理
                }
            }
            long endTime = System.currentTimeMillis();
            //响应参数toString
            String response = null;
            if (proceed instanceof Result) {
                Result result = (Result) proceed;
                response = result.toJson();
            } else if (proceed!=null){
                response = proceed.toString();
            }
            log.info("processing request:{}.{} end,params:{},response:{},use:{}ms", className, methodName, arguments, response, endTime - startTime);
            return proceed;
        }
    
        /**
         * 参数校验
         *
         * @param args
         * @param classes
         * @return
         */
        private Result validParams(Object[] args, Class[] classes) {
            Result errorResult = null;
            for (Object arg : args) {
                if(arg==null){
                    continue;
                }
                BindingResult bindingResult = new BeanPropertyBindingResult(arg, arg.getClass().getSimpleName());
                if(classes.length>0){
                    validator.validate(arg, bindingResult,classes);
                }else {
                    validator.validate(arg, bindingResult);
                }
                StringBuilder errorMsg = null;
                Map<String, String> errorMap = null;
                if (bindingResult.hasErrors()) {
                    errorMsg = new StringBuilder();
                    errorMsg.append(//异常信息);
                    final List<FieldError> fieldErrors = bindingResult.getFieldErrors();
                    errorMap = new HashMap<>(fieldErrors.size());
                    for (FieldError fieldError : fieldErrors) {
                        errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
                    }
                    errorMsg.append(JSON.toJSON(errorMap));
                    errorResult = new Result(...异常);
                }
            }
            return errorResult;
        }
    }

    三、使用

    使用时候只要在controller中的方法上加上这个注解就可以了

        @PostMapping("/create")
        @RestRequestHelper( validGroups = {XxxForm.Create.class})//validGroups为需要校验的分组,如果不需要分组校验,不写这个参数即可
        public Result create(@RequestBody XxxForm form) {
            //业务处理
        }

    分组校验不会的参考我另一篇文章:https://www.cnblogs.com/liuboyuan/p/11093383.html

  • 相关阅读:
    什么是php面向对象及面向对象的三大特性
    php类的定义与实例化方法
    php面向对象之$this->用法简述
    url的主要功能是什么
    PHP字符串比较函数详解
    PHP截取字符串函数substr()函数实例用法详解
    php 读取文件
    php 正则达达示中的模式修正符
    php正则表示中的元字符
    php 正则表达示中的原子
  • 原文地址:https://www.cnblogs.com/liuboyuan/p/12101944.html
Copyright © 2020-2023  润新知