开发者

SpringBoot集成Aviator实现参数校验的代码工程

目录
  • 1.什么是aviator?
    • Aviator的特性
    • Aviator的限制
    • 使用场景
  • 2.代码工程
    • 实验目的
    • pom.XML
    • controller
    • annotation
    • 全局异常拦截
    • 代码仓库
  • 3.测试
    • 4.引用

      1.什么是aviator?

      Aviator是一个高性能、轻量级的Java语言实现的表达式求值引擎,主要用于各种表达式的动态求值。现在已经有很多开源可用的java表达式求值引擎,为什么还需要Avaitor呢? Aviator的设计目标是轻量级高性能 ,相比于Groovy、Jruby的笨重,Aviator非常小,加上依赖包也才450K,不算依赖包的话只有70K;当然,Aviator的语法是受限的,它不是一门完整的语言,而只是语言的一小部分集合。 其次,Aviator的实现思路与其他轻量级的求值器很不相同,其他求值器一般都是通过解释的方式运行,而Aviator则是直接将表达式编译成Java字节码,交给JVM去执行。简单来说,Aviator的定位是介于Groovy这样的重量级脚本语言和IKExpression这样的轻量级表达式引擎之间

      Aviator的特性

      • 支持大部分运算操作符,包括算术操作符、关系运算符、逻辑操作符、位运算符、正则匹配操作符(=~)、三元表达式?: ,并且支持操作符的优先级和括号强制优先级,具体请看后面的操作符列表。
      • 支持大整数和精度运算(2.3.0版本引入)
      • 支持函数调用和自定义函数
      • 内置支持正则表达式匹配,类似Ruby、perl的匹配语法,并且支持类Ruby的$digit指向匹配分组。
      • 自动类型转换,当执行操作的时候,会自动判断操作数类型并做相应转换,无法转换即抛异常。
      • 支持传入变量,支持类似a.b.c的嵌套变量访问。
      • 函数式风格的seq库,操作集合和数组
      • 性能优秀

      Aviator的限制

      • 没有if else、do while等语句,没有赋值语句,仅支持逻辑表达式、算术表达式、三元表达式和正则匹配。
      • 不支持八进制数字字面量,仅支持十进制和十六进制数字字面量。

      使用场景

      • 规则判断以及规则引擎
      • 公式计算
      • 动态脚本控制

      2.代码工程

      实验目的

      利用aviator+aop实现参数校验

      pom.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
          <parent>
              <artifactId>springboot-demo</artifactId>
              <groupId>com.et</groupId>
              <version>1.0-SNAPSHOT</version>
          </parent>
          <modelVersion>4.0.0</modelVersion>
       
          &lphpt;artifactId>Aviator</artifactId>
       
          <properties>
              <maven.compiler.source>8</maven.compiler.source>
              <maven.compiler.target>8</maven.compiler.target>
          </properties>
          <dependencies>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spr编程客栈ing-boot-starter-web</artifactId>
              </dependency>
       
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-autoconfigure</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-test</artifactId>
                  <scope>test</scope>
              </dependency>
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
                  <optional>true</optional>
              </dependency>
              <!--AOP-->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-aop</artifactId>
              </dependency>
       
              <!--Aviator-->
              <dependency>
                  <groupId>com.googlecode.aviator</groupId>
                  <artifactId>aviator</artifactId>
                  <version>3.3.0</version>
              </dependency>
       
              <dependency>
                  <groupId>com.alibaba</groupId>
                  <artifactId>fastjson</artifactId>
                  <version>1.2.56</version>
              </dependency>
       
              <dependency>
                  <groupId>org.apache.commons</groupId>
                  <artifactId>commons-lang3</artifactId>
                  <version>3.8.1</version>
              </dependency>
       
          </dependencies>
      </project>

      controller

      在方法上加伤aviator校验规则

      package com.et.controller;
       
      import com.et.annotation.Check;
      import com.et.exception.HttpResult;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
       
      import java.util.HashMap;
      import java.util.Map;
       
      @RestController
      public class HelloWorldController {
          @RequestMapping("/hello")
          public Map<String, Object> showHelloWorld(){
              Map<String, Object> map = new HashMap<>();
              map.put("msg", "HelloWorld");
              return map;
          }
          @GetMapping("/simple")
          @Check(ex = "name != null", msg = "Name cannopythont be empty")
          @Check(ex = "age != null", msg = "Age cannot be empty")
          @Check(ex = "age > 18", msg = "Age must be over 18 years old")
          @Check(ex = "phone != null", msg = "phone cannot be empty")
          @Check(ex = "phone =~ /^(1)[0-9]{10}$/", msg = "The phone number format is incorrect")
          @Check(ex = "string.startsWith(phone,\"1\")", msg = "The phone number must start with 1")
          @Check(ex = "idCard != null", msg = "ID number cannot be empty")
          @Check(ex = "idCard =~ /^[1-9]\\d{5}[1-9]\\d{3}((0[1-9])||(1[0-2]))((0[1-9])||(1\\d)||(2\\d)||(3[0-1]))\\d{3}([0-9]||编程客栈X)$/", msg = "ID number format is incorrect")
          @Check(ex = "gender == 1", msg = "sex")
          @Check(ex = "date =~ /^[1-9][0-9]{3}-((0)[1-9]|(1)[0-2])-((0)[1-9]|[1,2][0-9]|(3)[0,1])$/", msg = "Wrong date format")
          @Check(ex = "date > '2019-12-20 00:00:00:00'", msg = "The date must be greater than 2019-12-20")
          public HttpResult simple(String name, Integer age, String phone, String idCard, String date) {
              System.out.println("name = " + name);
              System.out.println("age = " + age);
              System.out.println("phone = " + phone);
              System.out.println("idCard = " + idCard);
              System.out.println("date = " + date);
              return HttpResult.success();
          }
      }

      annotation

      单个规则注解

      package com.et.annotation;
       
      import java.lang.annotation.*;
       
       
      @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
      @Retention(RetentionPolicy.RUNTIME)
      //add more on a method
      @Repeatable(CheckContainer.class)
      public @interface Check {
       
         String ex() default "";
       
         String msg() default "";
       
      }

      多个规则注解

      package com.et.annotation;
       
      import java.lang.annotation.ElementType;
      import java.lang.annotation.Retention;
      import java.lang.annotation.RetentionPolicy;
      import java.lang.annotation.Target;
       
       
      @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
      @Retention(RetentionPolicy.RUNTIME)
      public @interface CheckContainer {
       
         Check[] value();
      }

      AOP拦截注解

      package com.et.annotation;
       
      import com.et.exception.UserFriendlyException;
      import com.googlecode.aviator.AviatorEvaLuator;
      import org.ASPectj.lang.JoinPoint;
      import org.aspectj.lang.annotation.Aspect;
      import org.aspectj.lang.annotation.Before;
      import org.aspectj.lang.annotation.Pointcut;
      import org.aspectj.lang.reflect.MethodSignature;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
      import org.springframework.util.StringUtils;
       
      import java.lang.reflect.Method;
      import java.util.*;
       
       
      @Aspect
      @Configuration
      public class AopConfig {
       
         /**
          * Aspects monitor multiple annotations, because one annotation is Check and multiple annotations are compiled to CheckContainer
          */
         @Pointcut("@annotation(com.et.annotation.CheckContainer) || @annotation(com.et.annotation.Check)")
         ppythonublic void pointcut() {
         }
       
         @Before("pointcut()")
         public Object before(JoinPoint point) {
            //get params
            Object[] args = point.getArgs();
            //get param name
            Method method = ((MethodSignature) point.getSignature()).getMethod();
            LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
            String[] paramNames = u.getParameterNames(method);
       
            CheckContainer checkContainer = method.getDeclaredAnnotation(CheckContainer.class);
            List<Check> value = new ArrayList<>();
       
            if (checkContainer != null) {
               value.addAll(Arrays.asList(checkContainer.value()));
            } else {
               Check check = method.getDeclaredAnnotation(Check.class);
               value.add(check);
            }
            for (int i = 0; i < value.size(); i++) {
               Check check = value.get(i);
               String ex = check.ex();
               //In the rule engine, null is represented by nil
               ex = ex.replaceAll("null", "nil");
               String msg = check.msg();
               if (StringUtils.isEmpty(msg)) {
                  msg = "server exception...";
               }
       
               Map<String, Object> map = new HashMap<>(16);
               for (int j = 0; j < paramNames.length; j++) {
                  //Prevent index out of bounds
                  if (j > args.length) {
                     continue;
                  }
                  map.put(paramNames[j], args[j]);
               }
               Boolean result = (Boolean) AviatorEvaluator.execute(ex, map);
               if (!result) {
                  throw new UserFriendlyException(msg);
               }
            }
            return null;
         }
      }

      全局异常拦截

      package com.et.exception;
       
      import com.alibaba.fastjson.JSON;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.http.HttpHeaders;
      import org.springframework.http.HttpStatus;
      import org.springframework.http.ResponseEntity;
      import org.springframework.lang.Nullable;
      import org.springframework.web.bind.annotation.ControllerAdvice;
      import org.springframework.web.bind.annotation.ExceptionHandler;
      import org.springframework.web.context.request.WebRequest;
      import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
       
      import javax.servlet.http.HttpServletRequest;
       
       
      @Configuration
      @ControllerAdvice
      public class DefaultGlobalExceptionHandler extends ResponseEntityExceptionHandler {
         private static final Logger LOGGER = LoggerFactory.getLogger(DefaultGlobalExceptionHandler.class);
       
         @Override
         protected ResponseEntity<Object> handleExceptionInternal(Exception ex, @Nullable Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
            HttpResult httpResult = HttpResult.failure(status.is5xxServerError() ? ErrorCode.serverError.getDesc() : ErrorCode.paramError.getDesc());
            LOGGER.error("handleException, ex caught, contextPath={}, httpResult={}, ex.msg={}", request.getContextPath(), JSON.toJSONString(httpResult), ex.getMessage());
            return super.handleExceptionInternal(ex, httpResult, headers, status, request);
         }
       
         @ExceptionHandler(Exception.class)
         protected ResponseEntity handleException(HttpServletRequest request, Exception ex) {
            boolean is5xxServerError;
            HttpStatus httpStatus;
            HttpResult httpResult;
            if (ex instanceof UserFriendlyException) {
               UserFriendlyException userFriendlyException = (UserFriendlyException) ex;
               is5xxServerError = userFriendlyException.getHttpStatusCode() >= 500;
               httpStatus = HttpStatus.valueOf(userFriendlyException.getHttpStatusCode());
               httpResult = HttpResult.failure(userFriendlyException.getErrorCode(), userFriendlyException.getMessage());
            } else if (ex instanceof IllegalArgumentException) {
               // Spring assertions are used in parameter judgment. requireTrue will throw an IllegalArgumentException. The client cannot handle 5xx exceptions, so 200 is still returned.
               httpStatus = HttpStatus.OK;
               is5xxServerError = false;
               httpResult = HttpResult.failure("Parameter verification error or data abnormality!");
            } else {
               httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
               is5xxServerError = true;
               httpResult = HttpResult.failure(ErrorCode.serverError.getDesc());
            }
            if (is5xxServerError) {
               LOGGER.error("handleException, ex caught, uri={}, httpResult={}", request.getRequestURI(), JSON.toJSONString(httpResult), ex);
            } else {
               LOGGER.error("handleException, ex caught, uri={}, httpResult={}, ex.msg={}", request.getRequestURI(), JSON.toJSONString(httpResult), ex.getMessage());
            }
            return new ResponseEntity<>(httpResult, httpStatus);
         }
       
      }

      以上只是一些关键代码,所有代码请参见下面代码仓库

      代码仓库

      • https://github.com/Harries/springboot-demo(aviator)

      3.测试

      • 启动Spring Boot应用
      • 访问 http://127.0.0.1:8088/simple?name=jack&age=12
      • 返回校验信息{"status":false,"code":4,"message":"Age must be over 18 years old","entry":null}

      4.引用

      https://github.com/killme2008/aviatorscript/blob/master/README-EN.md

      以上就是SpringBoot集成Aviator实现参数校验的代码工程的详细内容,更多关于SpringBoot Aviator参数校验的资料请关注编程客栈(www.devze.com)其它相关文章!

      0

      上一篇:

      下一篇:

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      最新开发

      开发排行榜