Forráskód Böngészése

增加参数验证、构建`json`结果增加参数

mxd 4 éve
szülő
commit
7209c3b2dd
21 módosított fájl, 539 hozzáadás és 168 törlés
  1. 6 0
      src/main/java/org/ssssssss/magicapi/adapter/Resource.java
  2. 27 3
      src/main/java/org/ssssssss/magicapi/adapter/resource/DatabaseResource.java
  3. 22 5
      src/main/java/org/ssssssss/magicapi/adapter/resource/RedisResource.java
  4. 2 1
      src/main/java/org/ssssssss/magicapi/controller/MagicAPIController.java
  5. 104 29
      src/main/java/org/ssssssss/magicapi/controller/RequestHandler.java
  6. 59 24
      src/main/java/org/ssssssss/magicapi/model/ApiInfo.java
  7. 144 0
      src/main/java/org/ssssssss/magicapi/model/BaseDefinition.java
  8. 66 0
      src/main/java/org/ssssssss/magicapi/model/DataType.java
  9. 11 0
      src/main/java/org/ssssssss/magicapi/model/Header.java
  10. 11 0
      src/main/java/org/ssssssss/magicapi/model/Parameter.java
  11. 4 4
      src/main/java/org/ssssssss/magicapi/modules/RequestModule.java
  12. 4 4
      src/main/java/org/ssssssss/magicapi/modules/ResponseModule.java
  13. 17 11
      src/main/java/org/ssssssss/magicapi/provider/ResultProvider.java
  14. 1 0
      src/main/java/org/ssssssss/magicapi/provider/StoreServiceProvider.java
  15. 1 0
      src/main/java/org/ssssssss/magicapi/provider/impl/DefaultGroupServiceProvider.java
  16. 2 2
      src/main/java/org/ssssssss/magicapi/provider/impl/DefaultMagicAPIService.java
  17. 6 2
      src/main/java/org/ssssssss/magicapi/provider/impl/DefaultResultProvider.java
  18. 10 3
      src/main/java/org/ssssssss/magicapi/script/ScriptManager.java
  19. 11 78
      src/main/java/org/ssssssss/magicapi/swagger/SwaggerProvider.java
  20. 12 2
      src/main/java/org/ssssssss/magicapi/utils/JsonUtils.java
  21. 19 0
      src/main/java/org/ssssssss/magicapi/utils/PatternUtils.java

+ 6 - 0
src/main/java/org/ssssssss/magicapi/adapter/Resource.java

@@ -64,6 +64,12 @@ public interface Resource {
 	 */
 	byte[] read();
 
+	/**
+	 * 读取当前资源下的所有内容,主要是缓存作用。
+	 */
+	default void readAll(){
+	}
+
 	/**
 	 * 获取子目录
 	 */

+ 27 - 3
src/main/java/org/ssssssss/magicapi/adapter/resource/DatabaseResource.java

@@ -1,10 +1,12 @@
 package org.ssssssss.magicapi.adapter.resource;
 
 import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.support.rowset.SqlRowSet;
 import org.ssssssss.magicapi.adapter.Resource;
 
 import java.nio.charset.StandardCharsets;
 import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
@@ -14,21 +16,43 @@ public class DatabaseResource extends KeyValueResource {
 
 	private final String tableName;
 
+	private Map<String,String> cachedContent = new ConcurrentHashMap<>();
+
 	public DatabaseResource(JdbcTemplate template, String tableName, String separator, String path, boolean readonly, KeyValueResource parent) {
 		super(separator, path, readonly, parent);
 		this.template = template;
 		this.tableName = tableName;
 	}
 
+	public DatabaseResource(JdbcTemplate template, String tableName, String separator, String path, boolean readonly, Map<String,String> cachedContent, KeyValueResource parent) {
+		this(template, tableName, separator, path, readonly, parent);
+		this.cachedContent = cachedContent;
+	}
+
 	@Override
 	public byte[] read() {
-		String sql = String.format("select file_content from %s where file_path = ?", tableName);
-		String value = template.queryForObject(sql, String.class, this.path);
+		String value = this.cachedContent.get(path);
+		if(value == null){
+			String sql = String.format("select file_content from %s where file_path = ?", tableName);
+			value = template.queryForObject(sql, String.class, this.path);
+		}
 		return value == null ? new byte[0] : value.getBytes(StandardCharsets.UTF_8);
 	}
 
+	@Override
+	public void readAll() {
+		String sql = String.format("select file_path, file_content from %s where file_path like '%s%%'", tableName, this.path);
+		SqlRowSet sqlRowSet = template.queryForRowSet(sql);
+		while (sqlRowSet.next()){
+			this.cachedContent.put(sqlRowSet.getString(1),sqlRowSet.getString(2));
+		}
+	}
+
 	@Override
 	public boolean exists() {
+		if(this.cachedContent.containsKey(this.path)){
+			return true;
+		}
 		String sql = String.format("select count(*) from %s where file_path = ?", tableName);
 		Long value = template.queryForObject(sql, Long.class, this.path);
 		return value != null && value > 0;
@@ -65,7 +89,7 @@ public class DatabaseResource extends KeyValueResource {
 
 	@Override
 	public Function<String, Resource> mappedFunction() {
-		return it -> new DatabaseResource(template, tableName, separator, it, readonly, this);
+		return it -> new DatabaseResource(template, tableName, separator, it, readonly, this.cachedContent,this);
 	}
 
 	@Override

+ 22 - 5
src/main/java/org/ssssssss/magicapi/adapter/resource/RedisResource.java

@@ -10,10 +10,8 @@ import org.ssssssss.magicapi.adapter.Resource;
 
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
 
 public class RedisResource extends KeyValueResource {
@@ -22,6 +20,8 @@ public class RedisResource extends KeyValueResource {
 
 	private static final Logger logger = LoggerFactory.getLogger(RedisResource.class);
 
+	private final Map<String,String> cachedContent = new ConcurrentHashMap<>();
+
 	public RedisResource(StringRedisTemplate redisTemplate, String path, String separator, boolean readonly, RedisResource parent) {
 		super(separator, path, readonly, parent);
 		this.redisTemplate = redisTemplate;
@@ -31,9 +31,23 @@ public class RedisResource extends KeyValueResource {
 		this(redisTemplate,path,separator,readonly,null);
 	}
 
+	@Override
+	public void readAll() {
+		List<String> keys = new ArrayList<>(keys());
+		List<String> values = redisTemplate.opsForValue().multiGet(keys);
+		if(values != null){
+			for (int i = 0,size = keys.size(); i < size; i++) {
+				this.cachedContent.put(keys.get(i),values.get(i));
+			}
+		}
+	}
+
 	@Override
 	public byte[] read() {
-		String value = redisTemplate.opsForValue().get(path);
+		String value = this.cachedContent.get(path);
+		if(value == null){
+			value = redisTemplate.opsForValue().get(path);
+		}
 		return value == null ? new byte[0] : value.getBytes(StandardCharsets.UTF_8);
 	}
 
@@ -51,6 +65,9 @@ public class RedisResource extends KeyValueResource {
 
 	@Override
 	public boolean exists() {
+		if(this.cachedContent.containsKey(this.path)){
+			return true;
+		}
 		return Boolean.TRUE.equals(this.redisTemplate.hasKey(this.path));
 	}
 

+ 2 - 1
src/main/java/org/ssssssss/magicapi/controller/MagicAPIController.java

@@ -3,6 +3,7 @@ package org.ssssssss.magicapi.controller;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.ssssssss.magicapi.config.MagicConfiguration;
@@ -149,7 +150,7 @@ public class MagicAPIController extends MagicController {
 	 */
 	@RequestMapping("/save")
 	@ResponseBody
-	public JsonBean<String> save(HttpServletRequest request, ApiInfo info) {
+	public JsonBean<String> save(HttpServletRequest request, @RequestBody ApiInfo info) {
 		if(configuration.getWorkspace().readonly()){
 			return new JsonBean<>(0, "当前为只读模式,无法保存");
 		}

+ 104 - 29
src/main/java/org/ssssssss/magicapi/controller/RequestHandler.java

@@ -26,32 +26,34 @@ import org.ssssssss.magicapi.context.SessionContext;
 import org.ssssssss.magicapi.interceptor.RequestInterceptor;
 import org.ssssssss.magicapi.logging.LogInfo;
 import org.ssssssss.magicapi.logging.MagicLoggerContext;
-import org.ssssssss.magicapi.model.ApiInfo;
-import org.ssssssss.magicapi.model.JsonBean;
-import org.ssssssss.magicapi.model.JsonBodyBean;
-import org.ssssssss.magicapi.model.Options;
+import org.ssssssss.magicapi.model.*;
 import org.ssssssss.magicapi.modules.ResponseModule;
 import org.ssssssss.magicapi.provider.ResultProvider;
 import org.ssssssss.magicapi.script.ScriptManager;
+import org.ssssssss.magicapi.utils.PatternUtils;
 import org.ssssssss.script.MagicScriptContext;
 import org.ssssssss.script.MagicScriptDebugContext;
 import org.ssssssss.script.exception.MagicScriptAssertException;
 import org.ssssssss.script.exception.MagicScriptException;
 import org.ssssssss.script.functions.ObjectConvertExtension;
 import org.ssssssss.script.parsing.Span;
+import org.ssssssss.script.parsing.ast.literal.BooleanLiteral;
+import org.ssssssss.script.reflection.JavaInvoker;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
 import java.util.*;
 import java.util.stream.Collectors;
 
 public class RequestHandler extends MagicController {
 
-	private static Logger logger = LoggerFactory.getLogger(RequestHandler.class);
+	private static final Logger logger = LoggerFactory.getLogger(RequestHandler.class);
 
-	private ResultProvider resultProvider;
+	private final ResultProvider resultProvider;
 
 	public RequestHandler(MagicConfiguration configuration) {
 		super(configuration);
@@ -74,11 +76,17 @@ public class RequestHandler extends MagicController {
 		}
 		if (info == null) {
 			logger.error("接口不存在");
-			return resultProvider.buildResult(1001, "fail", "接口不存在", requestTime);
+			return resultProvider.buildResult(null, request, response, 1001, "fail", "接口不存在", requestTime);
+		}
+		// 验证
+		Object value = doValidate(info, request, response, requestTime, parameters);
+		if (value != null) {
+			if (requestedFromTest) {
+				return new JsonBean<>(0, "参数验证失败", value);
+			}
+			return value;
 		}
 		MagicScriptContext context = createMagicScriptContext(info, request, pathVariables, parameters);
-
-		Object value;
 		// 执行前置拦截器
 		if ((value = doPreHandle(info, context, request, response)) != null) {
 			if (requestedFromTest) {
@@ -96,11 +104,78 @@ public class RequestHandler extends MagicController {
 		return invokeRequest(info, requestTime, context, request, response);
 	}
 
+	private Object doValidate(ApiInfo info, HttpServletRequest request, HttpServletResponse response, Long requestTime, Map<String, Object> parameters) {
+		List<Parameter> parameterList = info.getParameters();
+		for (Parameter parameter : parameterList) {
+			String requestValue = StringUtils.defaultIfBlank(Objects.toString(parameters.get(parameter.getName())), Objects.toString(parameter.getDefaultValue(), ""));
+			if (parameter.isRequired()) {
+				if (StringUtils.isBlank(requestValue)) {
+					return resultProvider.buildResult(info, request, response, 0, StringUtils.defaultIfBlank(parameter.getError(), String.format("参数[%s]为必填项", parameter.getName())), null, requestTime);
+				}
+			}
+			try {
+				Object value = convertValue(parameter.getDataType(), parameter.getName(), requestValue);
+				String validateType = parameter.getValidateType();
+				if ("pattern".equals(validateType)) {    // 正则验证
+					String expression = parameter.getExpression();
+					if (StringUtils.isNotBlank(expression) && !PatternUtils.match(Objects.toString(value, ""), expression)) {
+						return resultProvider.buildResult(info, request, response, 0, StringUtils.defaultIfBlank(parameter.getError(), String.format("参数[%s]不满足正则表达式", parameter.getName())), null, requestTime);
+					}
+				}
+				parameters.put(parameter.getName(), value);
+
+			} catch (Exception e) {
+				return resultProvider.buildResult(info, request, response, 0, StringUtils.defaultIfBlank(parameter.getError(), String.format("参数[%s]不合法", parameter.getName())), null, requestTime);
+			}
+		}
+		// 取出表达式验证的参数
+		List<Parameter> validates = parameterList.stream().filter(it -> "expression".equals(it.getValidateType()) && StringUtils.isNotBlank(it.getExpression())).collect(Collectors.toList());
+		for (Parameter parameter : validates) {
+			MagicScriptContext context = new MagicScriptContext();
+			context.putMapIntoContext(parameters);
+			context.set("value", parameters.get(parameter.getName()));
+			if (!BooleanLiteral.isTrue(ScriptManager.executeExpression(parameter.getExpression(), context))) {
+				return resultProvider.buildResult(info, request, response, 0, StringUtils.defaultIfBlank(parameter.getError(), String.format("参数[%s]不满足表达式", parameter.getName())), null, requestTime);
+			}
+		}
+		return null;
+	}
+
+	private Object convertValue(DataType dataType, String name, String value) {
+		if (dataType == null) {
+			return value;
+		}
+		try {
+			if (dataType.isNumber()) {
+				BigDecimal decimal = ObjectConvertExtension.asDecimal(value, null);
+				if (decimal == null) {
+					throw new IllegalArgumentException();
+				}
+				return dataType.getInvoker().invoke0(decimal, null);
+			} else {
+				JavaInvoker<Method> invoker = dataType.getInvoker();
+				if (invoker != null) {
+					List<Object> params = new ArrayList<>();
+					if (dataType.isNeedName()) {
+						params.add(name);
+					}
+					if (dataType.isNeedValue()) {
+						params.add(value);
+					}
+					return invoker.invoke0(null, null, params.toArray());
+				}
+			}
+			return value;
+		} catch (Throwable throwable) {
+			throw new IllegalArgumentException();
+		}
+	}
+
 	private Object invokeContinueRequest(ApiInfo info, long requestTime, HttpServletRequest request, HttpServletResponse response) throws Exception {
 		String sessionId = getRequestedSessionId(request);
 		MagicScriptDebugContext context = MagicScriptDebugContext.getDebugContext(sessionId);
 		if (context == null) {
-			return new JsonBean<>(0, "debug session not found!", resultProvider.buildResult(0, "debug session not found!", requestTime));
+			return new JsonBean<>(0, "debug session not found!", resultProvider.buildResult(info, request, response, 0, "debug session not found!", requestTime));
 		}
 		// 重置断点
 		context.setBreakpoints(getRequestedBreakpoints(request));
@@ -112,9 +187,9 @@ public class RequestHandler extends MagicController {
 			e.printStackTrace();
 		}
 		if (context.isRunning()) {    //判断是否执行完毕
-			return new JsonBodyBean<>(1000, context.getId(), resultProvider.buildResult(1000, context.getId(), requestTime), context.getDebugInfo());
+			return new JsonBodyBean<>(1000, context.getId(), resultProvider.buildResult(info, request, response, 1000, context.getId(), requestTime), context.getDebugInfo());
 		} else if (context.isException()) {
-			return resolveThrowable((Throwable) context.getReturnValue(), requestTime);
+			return resolveThrowable(info, request, response, (Throwable) context.getReturnValue(), requestTime);
 		}
 		Object value = context.getReturnValue();
 		// 执行后置拦截器
@@ -124,7 +199,7 @@ public class RequestHandler extends MagicController {
 			// 后置拦截器不包裹
 			return value;
 		}
-		return convertResult(context.getReturnValue(), requestTime, response);
+		return convertResult(info, request, context.getReturnValue(), requestTime, response);
 	}
 
 	private Object invokeTestRequest(ApiInfo info, long requestTime, MagicScriptDebugContext context, HttpServletRequest request, HttpServletResponse response) {
@@ -133,9 +208,9 @@ public class RequestHandler extends MagicController {
 			initializeDebug(context, request, response);
 			Object result = ScriptManager.executeScript(info.getScript(), context);
 			if (context.isRunning()) {
-				return new JsonBodyBean<>(1000, context.getId(), resultProvider.buildResult(1000, context.getId(), result, requestTime), result);
+				return new JsonBodyBean<>(1000, context.getId(), resultProvider.buildResult(info, request, response, 1000, context.getId(), result, requestTime), result);
 			} else if (context.isException()) {    //判断是否出现异常
-				return resolveThrowable((Throwable) context.getReturnValue(), requestTime);
+				return resolveThrowable(info, request, response, (Throwable) context.getReturnValue(), requestTime);
 			}
 			Object value = result;
 			// 执行后置拦截器
@@ -145,9 +220,9 @@ public class RequestHandler extends MagicController {
 				// 后置拦截器不包裹
 				return value;
 			}
-			return convertResult(result, requestTime, response);
+			return convertResult(info, request, result, requestTime, response);
 		} catch (Exception e) {
-			return resolveThrowable(e, requestTime);
+			return resolveThrowable(info, request, response, e, requestTime);
 		}
 	}
 
@@ -161,20 +236,20 @@ public class RequestHandler extends MagicController {
 				return value;
 			}
 			// 对返回结果包装处理
-			return response(result, requestTime);
+			return response(info, request, response, result, requestTime);
 		} catch (Throwable root) {
 			Throwable parent = root;
 			do {
 				if (parent instanceof MagicScriptAssertException) {
 					MagicScriptAssertException sae = (MagicScriptAssertException) parent;
-					return resultProvider.buildResult(sae.getCode(), sae.getMessage(), requestTime);
+					return resultProvider.buildResult(info, request, response, sae.getCode(), sae.getMessage(), requestTime);
 				}
 			} while ((parent = parent.getCause()) != null);
 			if (configuration.isThrowException()) {
 				throw root;
 			}
 			logger.error("接口{}请求出错", request.getRequestURI(), root);
-			return resultProvider.buildResult(-1, "系统内部出现错误", requestTime);
+			return resultProvider.buildResult(info, request, response, -1, "系统内部出现错误", requestTime);
 		} finally {
 			RequestContext.remove();
 		}
@@ -183,9 +258,9 @@ public class RequestHandler extends MagicController {
 	/**
 	 * 转换请求结果
 	 */
-	private Object convertResult(Object result, long requestTime, HttpServletResponse response) throws IOException {
+	private Object convertResult(ApiInfo info, HttpServletRequest request, Object result, long requestTime, HttpServletResponse response) throws IOException {
 		if (result instanceof ResponseEntity) {
-			ResponseEntity entity = (ResponseEntity) result;
+			ResponseEntity<?> entity = (ResponseEntity<?>) result;
 			List<String> headers = new ArrayList<>();
 			for (Map.Entry<String, List<String>> entry : entity.getHeaders().entrySet()) {
 				String key = entry.getKey();
@@ -203,7 +278,7 @@ public class RequestHandler extends MagicController {
 		} else if (result instanceof ResponseModule.NullValue) {
 			return new JsonBean<>(1, "empty.");
 		}
-		return new JsonBean<>(resultProvider.buildResult(result, requestTime));
+		return new JsonBean<>(resultProvider.buildResult(info, request, response, result, requestTime));
 	}
 
 	/**
@@ -227,13 +302,13 @@ public class RequestHandler extends MagicController {
 	/**
 	 * 解决异常
 	 */
-	private JsonBean<Object> resolveThrowable(Throwable root, long requestTime) {
+	private JsonBean<Object> resolveThrowable(ApiInfo info, HttpServletRequest request, HttpServletResponse response, Throwable root, long requestTime) {
 		MagicScriptException se = null;
 		Throwable parent = root;
 		do {
 			if (parent instanceof MagicScriptAssertException) {
 				MagicScriptAssertException sae = (MagicScriptAssertException) parent;
-				return new JsonBean<>(resultProvider.buildResult(sae.getCode(), sae.getMessage(), requestTime));
+				return new JsonBean<>(resultProvider.buildResult(info, request, response, sae.getCode(), sae.getMessage(), requestTime));
 			}
 			if (parent instanceof MagicScriptException) {
 				se = (MagicScriptException) parent;
@@ -242,9 +317,9 @@ public class RequestHandler extends MagicController {
 		logger.error("测试脚本出错", root);
 		if (se != null) {
 			Span.Line line = se.getLine();
-			return new JsonBodyBean<>(-1000, se.getSimpleMessage(), resultProvider.buildResult(-1000, se.getSimpleMessage(), requestTime), line == null ? null : Arrays.asList(line.getLineNumber(), line.getEndLineNumber(), line.getStartCol(), line.getEndCol()));
+			return new JsonBodyBean<>(-1000, se.getSimpleMessage(), resultProvider.buildResult(info, request, response, -1000, se.getSimpleMessage(), requestTime), line == null ? null : Arrays.asList(line.getLineNumber(), line.getEndLineNumber(), line.getStartCol(), line.getEndCol()));
 		}
-		return new JsonBean<>(-1, root.getMessage(), resultProvider.buildResult(-1, root.getMessage(), requestTime));
+		return new JsonBean<>(-1, root.getMessage(), resultProvider.buildResult(info, request, response, -1, root.getMessage(), requestTime));
 	}
 
 	private void initializeDebug(MagicScriptDebugContext context, HttpServletRequest request, HttpServletResponse response) {
@@ -336,13 +411,13 @@ public class RequestHandler extends MagicController {
 	/**
 	 * 包装返回结果
 	 */
-	private Object response(Object value, long requestTime) {
+	private Object response(ApiInfo info, HttpServletRequest request, HttpServletResponse response, Object value, long requestTime) {
 		if (value instanceof ResponseEntity) {
 			return value;
 		} else if (value instanceof ResponseModule.NullValue) {
 			return null;
 		}
-		return resultProvider.buildResult(value, requestTime);
+		return resultProvider.buildResult(info, request, response, value, requestTime);
 	}
 
 	/**

+ 59 - 24
src/main/java/org/ssssssss/magicapi/model/ApiInfo.java

@@ -1,11 +1,11 @@
 package org.ssssssss.magicapi.model;
 
+import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import org.ssssssss.magicapi.utils.JsonUtils;
 
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
 
 /**
  * 接口信息
@@ -25,7 +25,7 @@ public class ApiInfo extends MagicEntity{
 	/**
 	 * 设置的请求参数
 	 */
-	private String parameter;
+	private List<Parameter> parameters = Collections.emptyList();
 
 	/**
 	 * 设置的接口选项
@@ -40,7 +40,7 @@ public class ApiInfo extends MagicEntity{
 	/**
 	 * 请求头
 	 */
-	private String requestHeader;
+	private List<Header> headers = Collections.emptyList();
 
 	/**
 	 * 输出结果
@@ -57,7 +57,6 @@ public class ApiInfo extends MagicEntity{
 	 */
 	private JsonNode jsonNode;
 
-
 	public String getMethod() {
 		return method;
 	}
@@ -74,19 +73,42 @@ public class ApiInfo extends MagicEntity{
 		this.path = path;
 	}
 
-	public String getParameter() {
-		return parameter;
-	}
-
 	public void setParameter(String parameter) {
-		this.parameter = parameter;
+		if(parameter != null ){
+			parameter = parameter.trim();
+			if(parameter.startsWith("[")){	// v0.5.0+
+				this.parameters = JsonUtils.readValue(Objects.toString(parameter,"[]"), new TypeReference<List<Parameter>>() {});
+			}else{
+				Map map = JsonUtils.readValue(Objects.toString(parameter, "{}"), Map.class);
+				Object request = map.get("request");
+				if (request instanceof Map) {
+					Map requestMap = (Map) request;
+					Set keys = requestMap.keySet();
+					this.parameters = new ArrayList<>();
+					for (Object key : keys) {
+						this.parameters.add(new Parameter(key.toString(),Objects.toString(requestMap.get(key),"")));
+					}
+				}
+				Object header = map.get("header");
+				if (header instanceof Map) {
+					Map headers = (Map) header;
+					Set keys = headers.keySet();
+					this.headers = new ArrayList<>();
+					for (Object key : keys) {
+						this.headers.add(new Header(key.toString(),Objects.toString(headers.get(key),"")));
+					}
+				}
+				if (map.containsKey("body")) {
+					this.requestBody = Objects.toString(map.get("body"),null);
+				}
+			}
+		}
 	}
 
 	public String getResponseBody() {
 		return responseBody;
 	}
 
-
 	public String getRequestBody() {
 		return requestBody;
 	}
@@ -95,13 +117,6 @@ public class ApiInfo extends MagicEntity{
 		this.requestBody = requestBody;
 	}
 
-	public String getRequestHeader() {
-		return requestHeader;
-	}
-
-	public void setRequestHeader(String requestHeader) {
-		this.requestHeader = requestHeader;
-	}
 
 	public void setResponseBody(String responseBody) {
 		this.responseBody = responseBody;
@@ -133,6 +148,26 @@ public class ApiInfo extends MagicEntity{
 		return option;
 	}
 
+	public List<Parameter> getParameters() {
+		return parameters;
+	}
+
+	public void setParameters(List<Parameter> parameters) {
+		this.parameters = parameters;
+	}
+
+	public void setRequestHeader(String requestHeader){
+		this.headers = JsonUtils.readValue(Objects.toString(requestHeader,"[]"), new TypeReference<List<Header>>() {});
+	}
+
+	public List<Header> getHeaders() {
+		return headers;
+	}
+
+	public void setHeaders(List<Header> headers) {
+		this.headers = headers;
+	}
+
 	public void setOptionValue(String optionValue) {
 		this.setOption(optionValue);
 	}
@@ -173,10 +208,10 @@ public class ApiInfo extends MagicEntity{
 				Objects.equals(script, apiInfo.script) &&
 				Objects.equals(name, apiInfo.name) &&
 				Objects.equals(groupId, apiInfo.groupId) &&
-				Objects.equals(parameter, apiInfo.parameter) &&
+				Objects.equals(parameters, apiInfo.parameters) &&
 				Objects.equals(option, apiInfo.option) &&
 				Objects.equals(requestBody, apiInfo.requestBody) &&
-				Objects.equals(requestHeader, apiInfo.requestHeader) &&
+				Objects.equals(headers, apiInfo.headers) &&
 				Objects.equals(responseBody, apiInfo.responseBody) &&
 				Objects.equals(description, apiInfo.description);
 	}
@@ -184,7 +219,7 @@ public class ApiInfo extends MagicEntity{
 
 	@Override
 	public int hashCode() {
-		return Objects.hash(id, method, path, script, name, groupId, parameter, option, requestBody, requestHeader, responseBody, description);
+		return Objects.hash(id, method, path, script, name, groupId, parameters, option, requestBody, headers, responseBody, description);
 	}
 
 	public ApiInfo copy() {
@@ -195,10 +230,10 @@ public class ApiInfo extends MagicEntity{
 		info.setPath(this.path);
 		info.setScript(this.script);
 		info.setGroupId(this.groupId);
-		info.setParameter(parameter);
+		info.setParameters(this.parameters);
 		info.setOption(this.option);
 		info.setRequestBody(this.requestBody);
-		info.setRequestHeader(this.requestHeader);
+		info.setHeaders(this.headers);
 		info.setResponseBody(this.responseBody);
 		info.setDescription(this.description);
 		return info;

+ 144 - 0
src/main/java/org/ssssssss/magicapi/model/BaseDefinition.java

@@ -0,0 +1,144 @@
+package org.ssssssss.magicapi.model;
+
+import java.util.Objects;
+
+public class BaseDefinition {
+
+	/**
+	 * 名
+	 */
+	private String name;
+
+	/**
+	 * 值
+	 */
+	private String value;
+
+	/**
+	 * 描述
+	 */
+	private String description;
+
+	/**
+	 * 是否必填
+	 */
+	private boolean required;
+
+	/**
+	 * 数据类型
+	 */
+	private DataType dataType;
+
+	/**
+	 * 默认值
+	 */
+	private String defaultValue;
+
+	/**
+	 * 验证类型
+	 */
+	private String validateType;
+
+	/**
+	 * 验证说明
+	 */
+	private String error;
+
+	/**
+	 * 验证表达式
+	 */
+	private String expression;
+
+	public BaseDefinition() {
+	}
+
+	public BaseDefinition(String name, String value) {
+		this.name = name;
+		this.value = value;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getValue() {
+		return value;
+	}
+
+	public void setValue(String value) {
+		this.value = value;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
+	public boolean isRequired() {
+		return required;
+	}
+
+	public void setRequired(boolean required) {
+		this.required = required;
+	}
+
+	public DataType getDataType() {
+		return dataType;
+	}
+
+	public void setDataType(DataType dataType) {
+		this.dataType = dataType;
+	}
+
+	public String getDefaultValue() {
+		return defaultValue;
+	}
+
+	public void setDefaultValue(String defaultValue) {
+		this.defaultValue = defaultValue;
+	}
+
+	public String getValidateType() {
+		return validateType;
+	}
+
+	public void setValidateType(String validateType) {
+		this.validateType = validateType;
+	}
+
+	public String getError() {
+		return error;
+	}
+
+	public void setError(String error) {
+		this.error = error;
+	}
+
+	public String getExpression() {
+		return expression;
+	}
+
+	public void setExpression(String expression) {
+		this.expression = expression;
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if (this == o) return true;
+		if (!(o instanceof BaseDefinition)) return false;
+		BaseDefinition that = (BaseDefinition) o;
+		return required == that.required && Objects.equals(name, that.name) && Objects.equals(value, that.value) && Objects.equals(description, that.description) && dataType == that.dataType && Objects.equals(defaultValue, that.defaultValue) && Objects.equals(validateType, that.validateType) && Objects.equals(error, that.error) && Objects.equals(expression, that.expression);
+	}
+
+	@Override
+	public int hashCode() {
+		return Objects.hash(name, value, description, required, dataType, defaultValue, validateType, error, expression);
+	}
+}

+ 66 - 0
src/main/java/org/ssssssss/magicapi/model/DataType.java

@@ -0,0 +1,66 @@
+package org.ssssssss.magicapi.model;
+
+import org.ssssssss.magicapi.modules.RequestModule;
+import org.ssssssss.script.reflection.JavaInvoker;
+
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+
+import static org.ssssssss.script.reflection.JavaReflection.findInvoker;
+
+public enum DataType {
+	String,
+	Integer(true, findInvoker(BigDecimal.class, "intValue")),
+	Double(true, findInvoker(BigDecimal.class, "doubleValue")),
+	Long(true, findInvoker(BigDecimal.class, "longValue")),
+	Float(true, findInvoker(BigDecimal.class, "floatValue")),
+	Byte(true, findInvoker(BigDecimal.class, "byteValue")),
+	Short(true, findInvoker(BigDecimal.class, "shortValue")),
+	MultipartFile(findInvoker(RequestModule.class, "getFile", new Class<?>[]{String.class}), true, false),
+	MultipartFiles(findInvoker(RequestModule.class, "getFiles", new Class<?>[]{String.class}), true, false);
+
+	private boolean isNumber;
+
+	private JavaInvoker<Method> invoker;
+
+	private boolean needName;
+
+	private boolean needValue;
+
+	DataType(boolean isNumber, JavaInvoker<Method> invoker, boolean needName, boolean needValue) {
+		this.isNumber = isNumber;
+		this.invoker = invoker;
+		this.needName = needName;
+		this.needValue = needValue;
+	}
+
+	DataType(JavaInvoker<Method> invoker, boolean needName, boolean needValue) {
+		this.invoker = invoker;
+		this.needName = needName;
+		this.needValue = needValue;
+	}
+
+	DataType(boolean isNumber, JavaInvoker<Method> invoker) {
+		this.isNumber = isNumber;
+		this.invoker = invoker;
+	}
+
+	DataType() {
+	}
+
+	public boolean isNumber() {
+		return isNumber;
+	}
+
+	public JavaInvoker<Method> getInvoker() {
+		return invoker;
+	}
+
+	public boolean isNeedName() {
+		return needName;
+	}
+
+	public boolean isNeedValue() {
+		return needValue;
+	}
+}

+ 11 - 0
src/main/java/org/ssssssss/magicapi/model/Header.java

@@ -0,0 +1,11 @@
+package org.ssssssss.magicapi.model;
+
+public class Header extends BaseDefinition{
+
+	public Header() {
+	}
+
+	public Header(String name, String value) {
+		super(name, value);
+	}
+}

+ 11 - 0
src/main/java/org/ssssssss/magicapi/model/Parameter.java

@@ -0,0 +1,11 @@
+package org.ssssssss.magicapi.model;
+
+public class Parameter extends BaseDefinition{
+
+	public Parameter() {
+	}
+
+	public Parameter(String name, String value) {
+		super(name, value);
+	}
+}

+ 4 - 4
src/main/java/org/ssssssss/magicapi/modules/RequestModule.java

@@ -23,7 +23,7 @@ public class RequestModule {
 	 * @param name 参数名
 	 */
 	@Comment("获取文件")
-	public MultipartFile getFile(@Comment("参数名") String name) {
+	public static MultipartFile getFile(@Comment("参数名") String name) {
 		MultipartRequest request = getMultipartHttpServletRequest();
 		return request == null ? null : request.getFile(name);
 	}
@@ -34,7 +34,7 @@ public class RequestModule {
 	 * @param name 参数名
 	 */
 	@Comment("获取多个文件")
-	public List<MultipartFile> getFiles(@Comment("参数名") String name) {
+	public static List<MultipartFile> getFiles(@Comment("参数名") String name) {
 		MultipartRequest request = getMultipartHttpServletRequest();
 		return request == null ? null : request.getFiles(name);
 	}
@@ -72,11 +72,11 @@ public class RequestModule {
 	/**
 	 * 获取原生HttpServletRequest对象
 	 */
-	public HttpServletRequest get() {
+	public static HttpServletRequest get() {
 		return RequestContext.getHttpServletRequest();
 	}
 
-	private MultipartRequest getMultipartHttpServletRequest() {
+	private static MultipartRequest getMultipartHttpServletRequest() {
 		HttpServletRequest request = get();
 		if (request != null && request.getContentType() != null && request.getContentType().toLowerCase().startsWith("multipart/")) {
 			return WebUtils.getNativeRequest(request, MultipartRequest.class);

+ 4 - 4
src/main/java/org/ssssssss/magicapi/modules/ResponseModule.java

@@ -23,7 +23,7 @@ import java.util.Map;
  */
 public class ResponseModule {
 
-	private ResultProvider resultProvider;
+	private final ResultProvider resultProvider;
 
 	public ResponseModule(ResultProvider resultProvider) {
 		this.resultProvider = resultProvider;
@@ -36,7 +36,7 @@ public class ResponseModule {
 	 * @param values 数据内容
 	 */
 	@Comment("返回自定义分页结果")
-	public Object page(@Comment("总条数") long total, @Comment("当前结果集") List<Map<String,Object>> values) {
+	public Object page(@Comment("总条数") long total, @Comment("当前结果集") List<Map<String, Object>> values) {
 		return resultProvider.buildPageResult(total, values);
 	}
 
@@ -46,7 +46,7 @@ public class ResponseModule {
 	 * @param value json内容
 	 */
 	@Comment("自定义返回json内容")
-	public ResponseEntity json(@Comment("返回对象") Object value) {
+	public ResponseEntity<Object> json(@Comment("返回对象") Object value) {
 		return ResponseEntity.ok(value);
 	}
 
@@ -116,7 +116,7 @@ public class ResponseModule {
 	 */
 	@Comment("添加Cookie")
 	public ResponseModule addCookie(@Comment("Cookie名") String name, @Comment("Cookie值") String value,
-						  @Comment("Cookie选项,如`path`、`httpOnly`、`domain`、`maxAge`") Map<String, Object> options) {
+									@Comment("Cookie选项,如`path`、`httpOnly`、`domain`、`maxAge`") Map<String, Object> options) {
 		if (StringUtils.isNotBlank(name)) {
 			Cookie cookie = new Cookie(name, value);
 			if (options != null) {

+ 17 - 11
src/main/java/org/ssssssss/magicapi/provider/ResultProvider.java

@@ -2,12 +2,15 @@ package org.ssssssss.magicapi.provider;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.ssssssss.magicapi.model.ApiInfo;
 import org.ssssssss.magicapi.model.PageResult;
 import org.ssssssss.script.exception.MagicScriptAssertException;
 import org.ssssssss.script.exception.MagicScriptException;
 import org.ssssssss.script.functions.ObjectConvertExtension;
 import org.ssssssss.script.parsing.ast.statement.Exit;
 
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -22,13 +25,13 @@ public interface ResultProvider {
 	/**
 	 * 根据异常内容构建结果
 	 */
-	default Object buildResult(Throwable root,long requestTime) {
+	default Object buildResult(ApiInfo apiInfo, HttpServletRequest request, HttpServletResponse response, Throwable root, long requestTime) {
 		MagicScriptException se = null;
 		Throwable parent = root;
 		do {
 			if (parent instanceof MagicScriptAssertException) {
 				MagicScriptAssertException sae = (MagicScriptAssertException) parent;
-				return buildResult(sae.getCode(), sae.getMessage(), requestTime);
+				return buildResult(apiInfo, request, response, sae.getCode(), sae.getMessage(), requestTime);
 			}
 			if (parent instanceof MagicScriptException) {
 				se = (MagicScriptException) parent;
@@ -36,25 +39,28 @@ public interface ResultProvider {
 		} while ((parent = parent.getCause()) != null);
 		logger.error("调用接口出错", root);
 		if (se != null) {
-			return buildResult(-1, se.getSimpleMessage(), requestTime);
+			return buildResult(apiInfo, request, response, -1, se.getSimpleMessage(), requestTime);
 		}
-		return buildResult(-1, root.getMessage(), requestTime);
+		return buildResult(apiInfo, request, response, -1, root.getMessage(), requestTime);
 	}
 
 	/**
 	 * 构建JSON返回结果(默认状态码和状态说明)
 	 *
-	 * @param data 数据内容,状态码和状态说明默认为1 "success"
+	 * @param apiInfo  接口信息,可能为NULL
+	 * @param request  可能为NULL
+	 * @param response 可能为NULL
+	 * @param data     数据内容,状态码和状态说明默认为1 "success"
 	 */
-	default Object buildResult(Object data, long requestTime) {
+	default Object buildResult(ApiInfo apiInfo, HttpServletRequest request, HttpServletResponse response, Object data, long requestTime) {
 		if (data instanceof Exit.Value) {
 			Exit.Value exitValue = (Exit.Value) data;
 			Object[] values = exitValue.getValues();
 			int code = values.length > 0 ? ObjectConvertExtension.asInt(values[0], 1) : 1;
 			String message = values.length > 1 ? Objects.toString(values[1], "success") : "success";
-			return buildResult(code, message, values.length > 2 ? values[2] : null,requestTime);
+			return buildResult(apiInfo, request, response, code, message, values.length > 2 ? values[2] : null, requestTime);
 		}
-		return buildResult(1, "success", data, requestTime);
+		return buildResult(apiInfo, request, response, 1, "success", data, requestTime);
 	}
 
 	/**
@@ -63,8 +69,8 @@ public interface ResultProvider {
 	 * @param code    状态码
 	 * @param message 状态说明
 	 */
-	default Object buildResult(int code, String message, long requestTime) {
-		return buildResult(code, message, null, requestTime);
+	default Object buildResult(ApiInfo apiInfo, HttpServletRequest request, HttpServletResponse response, int code, String message, long requestTime) {
+		return buildResult(apiInfo, request, response, code, message, null, requestTime);
 	}
 
 	/**
@@ -74,7 +80,7 @@ public interface ResultProvider {
 	 * @param message 状态说明
 	 * @param data    数据内容,可以通过data的类型判断是否是分页结果进行区分普通结果集和分页结果集
 	 */
-	Object buildResult(int code, String message, Object data, long requestTime);
+	Object buildResult(ApiInfo apiInfo, HttpServletRequest request, HttpServletResponse response, int code, String message, Object data, long requestTime);
 
 	/**
 	 * @param total 总数

+ 1 - 0
src/main/java/org/ssssssss/magicapi/provider/StoreServiceProvider.java

@@ -30,6 +30,7 @@ public abstract class StoreServiceProvider<T extends MagicEntity> {
 	public StoreServiceProvider(Class<T> clazz, Resource workspace, GroupServiceProvider groupServiceProvider) {
 		this.clazz = clazz;
 		this.workspace = workspace;
+		this.workspace.readAll();
 		this.groupServiceProvider = groupServiceProvider;
 		if(!this.workspace.exists()){
 			this.workspace.mkdir();

+ 1 - 0
src/main/java/org/ssssssss/magicapi/provider/impl/DefaultGroupServiceProvider.java

@@ -24,6 +24,7 @@ public class DefaultGroupServiceProvider implements GroupServiceProvider {
 
 	public DefaultGroupServiceProvider(Resource workspace) {
 		this.workspace = workspace;
+		this.workspace.readAll();
 	}
 
 	@Override

+ 2 - 2
src/main/java/org/ssssssss/magicapi/provider/impl/DefaultMagicAPIService.java

@@ -80,14 +80,14 @@ public class DefaultMagicAPIService implements MagicAPIService {
 	public Object call(String method, String path, Map<String, Object> context) {
 		long requestTime = System.currentTimeMillis();
 		try {
-			return resultProvider.buildResult(execute(method, path, context), requestTime);
+			return resultProvider.buildResult(null, null, null, execute(method, path, context), requestTime);
 		} catch (MagicServiceException e) {
 			return null;    //找不到对应接口
 		} catch (Throwable root) {
 			if (throwException) {
 				throw root;
 			}
-			return resultProvider.buildResult(root, requestTime);
+			return resultProvider.buildResult(null, null, null, root, requestTime);
 		}
 	}
 

+ 6 - 2
src/main/java/org/ssssssss/magicapi/provider/impl/DefaultResultProvider.java

@@ -1,12 +1,16 @@
 package org.ssssssss.magicapi.provider.impl;
 
+import org.ssssssss.magicapi.model.ApiInfo;
 import org.ssssssss.magicapi.model.JsonBean;
 import org.ssssssss.magicapi.provider.ResultProvider;
 
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
 public class DefaultResultProvider implements ResultProvider {
 
 	@Override
-	public Object buildResult(int code, String message, Object data, long requestTime) {
-		return new JsonBean<>(code, message, data, (int)(System.currentTimeMillis() - requestTime));
+	public Object buildResult(ApiInfo apiInfo, HttpServletRequest request, HttpServletResponse response, int code, String message, Object data, long requestTime) {
+		return new JsonBean<>(code, message, data, (int) (System.currentTimeMillis() - requestTime));
 	}
 }

+ 10 - 3
src/main/java/org/ssssssss/magicapi/script/ScriptManager.java

@@ -20,7 +20,7 @@ public class ScriptManager {
 	/**
 	 * 编译缓存
 	 */
-	private static DefaultSqlCache compileCache = new DefaultSqlCache(500, -1);
+	private static final DefaultSqlCache compileCache = new DefaultSqlCache(500, -1);
 
 	/**
 	 * 编译脚本
@@ -47,11 +47,18 @@ public class ScriptManager {
 		return compile("MagicScript", script).eval(simpleScriptContext);
 	}
 
-	public static ScriptEngine getEngine(String engine){
+	/**
+	 * 执行脚本
+	 */
+	public static Object executeExpression(String script, MagicScriptContext context) {
+		return executeScript("/* generated by execute expression */ return " + script, context);
+	}
+
+	public static ScriptEngine getEngine(String engine) {
 		return scriptEngineManager.getEngineByName(engine);
 	}
 
-	public static List<ScriptInfo> engines(){
+	public static List<ScriptInfo> engines() {
 		return scriptEngineManager.getEngineFactories().stream().map(ScriptInfo::new).collect(Collectors.toList());
 	}
 }

+ 11 - 78
src/main/java/org/ssssssss/magicapi/swagger/SwaggerProvider.java

@@ -7,9 +7,10 @@ import org.ssssssss.magicapi.config.MappingHandlerMapping;
 import org.ssssssss.magicapi.model.ApiInfo;
 import org.ssssssss.magicapi.provider.GroupServiceProvider;
 
-import java.io.IOException;
-import java.util.*;
-import java.util.stream.Collectors;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 
 /**
  * 生成swagger用的json
@@ -99,84 +100,16 @@ public class SwaggerProvider {
 	}
 
 	private List<SwaggerEntity.Parameter> parseParameters(ObjectMapper mapper, ApiInfo info) {
-		String content = info.getParameter();
-		content = content.trim();
 		List<SwaggerEntity.Parameter> parameters = new ArrayList<>();
-		if (content.startsWith("[")) {    // 0.5.0
-			try {
-				List<KeyValueDescription> kvs = readKeyValues(mapper, content);
-				parameters.addAll(kvs.stream().map(kv -> new SwaggerEntity.Parameter(kv.getName(), "query", "string", kv.getDescription(), kv.getValue())).collect(Collectors.toList()));
-				kvs = readKeyValues(mapper, Objects.toString(info.getRequestHeader(),"[]"));
-				parameters.addAll(kvs.stream().map(kv -> new SwaggerEntity.Parameter(kv.getName(), "header", "string", kv.getDescription(), kv.getValue())).collect(Collectors.toList()));
-				Object object = mapper.readValue(info.getRequestBody(),Object.class);
-				if(object instanceof List || object instanceof Map){
-					parameters.add(new SwaggerEntity.Parameter("body", "body", object instanceof List ? "array": "object", null, object));
-				}
-			} catch (Exception ignored) {
-			}
-		} else {
-			try {
-				Map map = mapper.readValue(Objects.toString(content, "{}"), Map.class);
-				Object request = map.get("request");
-				if (request instanceof Map) {
-					Map requestMap = (Map) request;
-					Set keys = requestMap.keySet();
-					for (Object key : keys) {
-						parameters.add(new SwaggerEntity.Parameter(key.toString(), "query", "string", key.toString(), requestMap.getOrDefault(key, "")));
-					}
-				}
-				Object header = map.get("header");
-				if (header instanceof Map) {
-					Map headers = (Map) header;
-					Set keys = headers.keySet();
-					for (Object key : keys) {
-						parameters.add(new SwaggerEntity.Parameter(key.toString(), "header", "string", key.toString(), headers.getOrDefault(key, "")));
-					}
-				}
-				if (map.containsKey("body")) {
-					parameters.add(new SwaggerEntity.Parameter("body", "body", null, null, map.get("body")));
-				}
-			} catch (Exception ignored) {
+		info.getParameters().forEach(it-> parameters.add(new SwaggerEntity.Parameter(it.getName(),"query","string",it.getDescription(),it.getValue())));
+		info.getHeaders().forEach(it-> parameters.add(new SwaggerEntity.Parameter(it.getName(),"header","string",it.getDescription(),it.getValue())));
+		try {
+			Object object = mapper.readValue(info.getRequestBody(),Object.class);
+			if(object instanceof List || object instanceof Map){
+				parameters.add(new SwaggerEntity.Parameter("body", "body", object instanceof List ? "array": "object", null, object));
 			}
+		} catch (Exception ignored) {
 		}
 		return parameters;
 	}
-
-	private List<KeyValueDescription> readKeyValues(ObjectMapper mapper, String json) throws IOException {
-		return Arrays.asList(mapper.readValue(json, KeyValueDescription[].class));
-	}
-
-	static class KeyValueDescription {
-
-		private String name;
-
-		private String value;
-
-		private String description;
-
-		public String getName() {
-			return name;
-		}
-
-		public void setName(String name) {
-			this.name = name;
-		}
-
-		public String getValue() {
-			return value;
-		}
-
-		public void setValue(String value) {
-			this.value = value;
-		}
-
-		public String getDescription() {
-			return description;
-		}
-
-		public void setDescription(String description) {
-			this.description = description;
-		}
-	}
-
 }

+ 12 - 2
src/main/java/org/ssssssss/magicapi/utils/JsonUtils.java

@@ -1,6 +1,7 @@
 package org.ssssssss.magicapi.utils;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.DeserializationFeature;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.slf4j.Logger;
@@ -10,9 +11,9 @@ import java.io.IOException;
 
 public class JsonUtils {
 
-	private static ObjectMapper mapper = new ObjectMapper();
+	private static final ObjectMapper mapper = new ObjectMapper();
 
-	private static Logger logger = LoggerFactory.getLogger(JsonUtils.class);
+	private static final Logger logger = LoggerFactory.getLogger(JsonUtils.class);
 
 	static{
 		mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
@@ -27,6 +28,15 @@ public class JsonUtils {
 		}
 	}
 
+	public static <T> T readValue(String json, TypeReference<T> typeReference){
+		try {
+			return mapper.readValue(json,typeReference);
+		} catch (IOException e) {
+			logger.error("读取json失败",e);
+			return null;
+		}
+	}
+
 	public static <T> T readValue(String json, Class<T> clazz){
 		try {
 			return mapper.readValue(json, clazz);

+ 19 - 0
src/main/java/org/ssssssss/magicapi/utils/PatternUtils.java

@@ -0,0 +1,19 @@
+package org.ssssssss.magicapi.utils;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Pattern;
+
+public class PatternUtils {
+
+	private static final Map<String, Pattern> cachedPatterns = new ConcurrentHashMap<>();
+
+	public static boolean match(String content,String regex){
+		Pattern pattern = cachedPatterns.get(regex);
+		if(pattern == null){
+			pattern = Pattern.compile(regex);
+			cachedPatterns.put(regex,pattern);
+		}
+		return pattern.matcher(content).find();
+	}
+}