Переглянути джерело

兼容从低版本升级上来的参数验证、文档生成。兼容`magic-script`的`asm`分支

mxd 3 роки тому
батько
коміт
954f88a7e6

+ 6 - 5
magic-api/src/main/java/org/ssssssss/magicapi/controller/MagicWebSocketDispatcher.java

@@ -12,6 +12,7 @@ import org.ssssssss.magicapi.model.Constants;
 import org.ssssssss.magicapi.model.MagicConsoleSession;
 import org.ssssssss.magicapi.model.MagicNotify;
 import org.ssssssss.magicapi.provider.MagicNotifyService;
+import org.ssssssss.magicapi.utils.Invoker;
 import org.ssssssss.magicapi.utils.JsonUtils;
 import org.ssssssss.script.reflection.MethodInvoker;
 
@@ -26,7 +27,7 @@ public class MagicWebSocketDispatcher extends TextWebSocketHandler {
 
 	private static final Logger logger = LoggerFactory.getLogger(MagicWebSocketDispatcher.class);
 
-	private static final Map<String, MethodInvoker> handlers = new HashMap<>();
+	private static final Map<String, Invoker> handlers = new HashMap<>();
 
 	private final String instanceId;
 
@@ -39,7 +40,7 @@ public class MagicWebSocketDispatcher extends TextWebSocketHandler {
 		WebSocketSessionManager.setInstanceId(instanceId);
 		websocketMessageHandlers.forEach(websocketMessageHandler ->
 				Stream.of(websocketMessageHandler.getClass().getDeclaredMethods())
-						.forEach(method -> handlers.put(method.getAnnotation(Message.class).value().name().toLowerCase(), new MethodInvoker(method, websocketMessageHandler)))
+						.forEach(method -> handlers.put(method.getAnnotation(Message.class).value().name().toLowerCase(), Invoker.from(new MethodInvoker(method, websocketMessageHandler))))
 		);
 	}
 
@@ -63,14 +64,14 @@ public class MagicWebSocketDispatcher extends TextWebSocketHandler {
 		// messageType[, data][,data]
 		int index = payload.indexOf(",");
 		String msgType = index == -1 ? payload : payload.substring(0, index);
-		MethodInvoker invoker = handlers.get(msgType);
+		Invoker invoker = handlers.get(msgType);
 		if (invoker != null) {
 			Object returnValue;
 			try {
 				Class<?>[] pTypes = invoker.getParameterTypes();
 				int pCount = pTypes.length;
 				if (pCount == 0) {
-					returnValue = invoker.invoke0(null, null, EMPTY_OBJECT_ARRAY);
+					returnValue = invoker.invoke(null, null, EMPTY_OBJECT_ARRAY);
 				} else {
 					Object[] pValues = new Object[pCount];
 					for (int i = 0; i < pCount; i++) {
@@ -88,7 +89,7 @@ public class MagicWebSocketDispatcher extends TextWebSocketHandler {
 							pValues[i] = JsonUtils.readValue(payload, pType);
 						}
 					}
-					returnValue =  invoker.invoke0(null, null, pValues);
+					returnValue =  invoker.invoke(null, null, pValues);
 				}
 				return returnValue;
 			} catch (Throwable e) {

+ 13 - 9
magic-api/src/main/java/org/ssssssss/magicapi/controller/RequestHandler.java

@@ -9,6 +9,7 @@ import org.springframework.http.ResponseEntity;
 import org.springframework.http.converter.HttpMessageConverter;
 import org.springframework.http.converter.HttpMessageNotReadableException;
 import org.springframework.http.server.ServletServerHttpRequest;
+import org.springframework.util.CollectionUtils;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
@@ -26,6 +27,7 @@ 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.Invoker;
 import org.ssssssss.magicapi.utils.PatternUtils;
 import org.ssssssss.script.MagicScriptContext;
 import org.ssssssss.script.MagicScriptDebugContext;
@@ -97,8 +99,8 @@ public class RequestHandler extends MagicController {
 			doValidate("header", requestEntity.getApiInfo().getHeaders(), headers, HEADER_INVALID);
 			// 验证 path
 			doValidate("path", paths, requestEntity.getPathVariables(), PATH_VARIABLE_INVALID);
-			BaseDefinition requestBody = SerializationUtils.clone(requestEntity.getApiInfo().getRequestBodyDefinition());
-			if (requestBody != null && requestBody.getChildren().size() > 0) {
+			BaseDefinition requestBody = requestEntity.getApiInfo().getRequestBodyDefinition();
+			if (requestBody != null && !CollectionUtils.isEmpty(requestBody.getChildren())) {
 				requestBody.setName(StringUtils.defaultIfBlank(requestBody.getName(), "root"));
 				doValidate(VAR_NAME_REQUEST_BODY, Collections.singletonList(requestBody), new HashMap<String, Object>() {{
 					put(requestBody.getName(), bodyValue);
@@ -151,21 +153,23 @@ public class RequestHandler extends MagicController {
 
 	private <T extends BaseDefinition> Map<String, Object> doValidate(String comment, List<T> validateParameters, Map<String, Object> parameters, JsonCode jsonCode) {
 		parameters = parameters != null ? parameters : EMPTY_MAP;
+		if(CollectionUtils.isEmpty(validateParameters)){
+			return parameters;
+		}
 		for (BaseDefinition parameter : validateParameters) {
 			// 针对requestBody多层级的情况
-			if (VAR_NAME_REQUEST_BODY_VALUE_TYPE_OBJECT.equalsIgnoreCase(parameter.getDataType().getJavascriptType())) {
+			if (DataType.Object == parameter.getDataType()) {
 				if (doValidateBody(comment, parameter, parameters, jsonCode, Map.class)) {
 					continue;
 				}
 				doValidate(VAR_NAME_REQUEST_BODY, parameter.getChildren(), (Map) parameters.get(parameter.getName()), jsonCode);
-			} else if (VAR_NAME_REQUEST_BODY_VALUE_TYPE_ARRAY.equalsIgnoreCase(parameter.getDataType().getJavascriptType())) {
+			} else if (DataType.Array == parameter.getDataType()) {
 				if (doValidateBody(comment, parameter, parameters, jsonCode, List.class)) {
 					continue;
 				}
 				List<Object> list = (List) parameters.get(parameter.getName());
 				if (list != null) {
-					List<BaseDefinition> definitions = parameter.getChildren();
-					List<Map<String, Object>> newList = list.stream().map(it -> doValidate(VAR_NAME_REQUEST_BODY, definitions, new HashMap<String, Object>() {{    // 使用 hashmap
+					List<Map<String, Object>> newList = list.stream().map(it -> doValidate(VAR_NAME_REQUEST_BODY, parameter.getChildren(), new HashMap<String, Object>() {{    // 使用 hashmap
 						put(EMPTY, it);
 					}}, jsonCode)).collect(Collectors.toList());
 					for (int i = 0, size = newList.size(); i < size; i++) {
@@ -234,9 +238,9 @@ public class RequestHandler extends MagicController {
 				if (decimal == null) {
 					throw new IllegalArgumentException();
 				}
-				return dataType.getInvoker().invoke0(decimal, null, EMPTY_OBJECT_ARRAY);
+				return dataType.getInvoker().invoke(decimal, null, EMPTY_OBJECT_ARRAY);
 			} else {
-				JavaInvoker<Method> invoker = dataType.getInvoker();
+				Invoker invoker = dataType.getInvoker();
 				if (invoker != null) {
 					List<Object> params = new ArrayList<>();
 					if (dataType.isNeedName()) {
@@ -245,7 +249,7 @@ public class RequestHandler extends MagicController {
 					if (dataType.isNeedValue()) {
 						params.add(value);
 					}
-					return invoker.invoke0(null, null, params.toArray());
+					return invoker.invoke(null, null, params.toArray());
 				}
 			}
 			return value;

+ 1 - 2
magic-api/src/main/java/org/ssssssss/magicapi/model/BaseDefinition.java

@@ -4,8 +4,7 @@ import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Objects;
 
-public class BaseDefinition implements Serializable {
-    private static final long serialVersionUID = 1L;
+public class BaseDefinition {
 	/**
 	 * 名
 	 */

+ 4 - 3
magic-api/src/main/java/org/ssssssss/magicapi/model/DataType.java

@@ -1,6 +1,7 @@
 package org.ssssssss.magicapi.model;
 
 import org.ssssssss.magicapi.modules.RequestModule;
+import org.ssssssss.magicapi.utils.Invoker;
 import org.ssssssss.script.reflection.JavaInvoker;
 
 import java.lang.reflect.Method;
@@ -25,7 +26,7 @@ public enum DataType {
 
 	private boolean isNumber;
 
-	private JavaInvoker<Method> invoker;
+	private Invoker invoker;
 
 	private boolean needName;
 
@@ -35,7 +36,7 @@ public enum DataType {
 
 	DataType(boolean isNumber, JavaInvoker<Method> invoker, boolean needName, boolean needValue, String javascriptType) {
 		this.isNumber = isNumber;
-		this.invoker = invoker;
+		this.invoker = Invoker.from(invoker);
 		this.needName = needName;
 		this.needValue = needValue;
 		this.javascriptType = javascriptType;
@@ -58,7 +59,7 @@ public enum DataType {
 		return isNumber;
 	}
 
-	public JavaInvoker<Method> getInvoker() {
+	public Invoker getInvoker() {
 		return invoker;
 	}
 

+ 9 - 8
magic-api/src/main/java/org/ssssssss/magicapi/modules/MongoModule.java

@@ -9,6 +9,7 @@ import org.slf4j.LoggerFactory;
 import org.springframework.data.mongodb.core.MongoTemplate;
 import org.ssssssss.magicapi.config.MagicModule;
 import org.ssssssss.magicapi.model.Constants;
+import org.ssssssss.magicapi.utils.Invoker;
 import org.ssssssss.script.reflection.JavaInvoker;
 import org.ssssssss.script.reflection.JavaReflection;
 
@@ -23,18 +24,18 @@ public class MongoModule extends HashMap<String, Object> implements MagicModule
 	private static final Logger logger = LoggerFactory.getLogger(MongoModule.class);
 
 	private final MongoTemplate mongoTemplate;
-	private final JavaInvoker<Method> mongoDbFactoryInvoker;
-	private JavaInvoker<Method> invoker;
+	private final Invoker mongoDbFactoryInvoker;
+	private Invoker invoker;
 
 	public MongoModule(MongoTemplate mongoTemplate) {
 		this.mongoTemplate = mongoTemplate;
-		mongoDbFactoryInvoker = JavaReflection.getMethod(this.mongoTemplate, "getMongoDbFactory");
+		mongoDbFactoryInvoker = Invoker.from(JavaReflection.getMethod(this.mongoTemplate, "getMongoDbFactory"));
 		if (mongoDbFactoryInvoker != null) {
 			try {
-				Object factory = mongoDbFactoryInvoker.invoke0(this.mongoTemplate, null, Constants.EMPTY_OBJECT_ARRAY);
-				invoker = JavaReflection.getMethod(factory, "getDb", StringUtils.EMPTY);
+				Object factory = mongoDbFactoryInvoker.invoke(this.mongoTemplate, null, Constants.EMPTY_OBJECT_ARRAY);
+				invoker = Invoker.from(JavaReflection.getMethod(factory, "getDb", StringUtils.EMPTY));
 				if (invoker == null) {
-					invoker = JavaReflection.getMethod(factory, "getMongoDatabase", StringUtils.EMPTY);
+					invoker = Invoker.from(JavaReflection.getMethod(factory, "getMongoDatabase", StringUtils.EMPTY));
 				}
 			} catch (Throwable e) {
 				logger.error("mongo模块初始化失败", e);
@@ -53,8 +54,8 @@ public class MongoModule extends HashMap<String, Object> implements MagicModule
 					return null;
 				}
 				try {
-					Object factory = mongoDbFactoryInvoker.invoke0(mongoTemplate, null, Constants.EMPTY_OBJECT_ARRAY);
-					MongoDatabase database = (MongoDatabase) invoker.invoke0(factory, null, new Object[]{databaseName.toString()});
+					Object factory = mongoDbFactoryInvoker.invoke(mongoTemplate, null, Constants.EMPTY_OBJECT_ARRAY);
+					MongoDatabase database = (MongoDatabase) invoker.invoke(factory, null, new Object[]{databaseName.toString()});
 					return database.getCollection(collection.toString());
 				} catch (Throwable throwable) {
 					throw new RuntimeException(throwable);

+ 44 - 42
magic-api/src/main/java/org/ssssssss/magicapi/swagger/SwaggerProvider.java

@@ -2,6 +2,7 @@ package org.ssssssss.magicapi.swagger;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.commons.lang3.StringUtils;
+import org.springframework.util.CollectionUtils;
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.ssssssss.magicapi.config.MappingHandlerMapping;
 import org.ssssssss.magicapi.model.ApiInfo;
@@ -35,13 +36,13 @@ public class SwaggerProvider {
 	/**
 	 * swagger Model定义路径前缀
 	 */
-    private static final String DEFINITION = "#/definitions/";
-    /**
+	private static final String DEFINITION = "#/definitions/";
+	/**
 	 * body空对象
-     */
-    private static final String BODY_EMPTY = "{}";
+	 */
+	private static final String BODY_EMPTY = "{}";
 
-    private final Map<String, Object> DEFINITION_MAP = new ConcurrentHashMap<>();
+	private final Map<String, Object> DEFINITION_MAP = new ConcurrentHashMap<>();
 
 	public void setMappingHandlerMapping(MappingHandlerMapping mappingHandlerMapping) {
 		this.mappingHandlerMapping = mappingHandlerMapping;
@@ -77,19 +78,19 @@ public class SwaggerProvider {
 				List<Map<String, Object>> parameters = parseParameters(mapper, info);
 				hasBody = parameters.stream().anyMatch(it -> VAR_NAME_REQUEST_BODY.equals(it.get("in")));
 				BaseDefinition baseDefinition = info.getRequestBodyDefinition();
-                if (hasBody && baseDefinition != null) {
-                    doProcessDefinition(baseDefinition, info, "root_", "request", 0);
-                }
+				if (hasBody && baseDefinition != null) {
+					doProcessDefinition(baseDefinition, info, "root_", "request", 0);
+				}
 				baseDefinition = info.getResponseBodyDefinition();
 				parameters.forEach(path::addParameter);
-                if(baseDefinition != null){
+				if (baseDefinition != null) {
 					Map responseMap = parseResponse(info);
 					if (!responseMap.isEmpty()) {
 						path.setResponses(responseMap);
 						doProcessDefinition(baseDefinition, info, "root_" + baseDefinition.getName(), "response", 0);
 					}
-				}else{
-					 path.addResponse("200", mapper.readValue(Objects.toString(info.getResponseBody(), BODY_EMPTY), Object.class));
+				} else {
+					path.addResponse("200", mapper.readValue(Objects.toString(info.getResponseBody(), BODY_EMPTY), Object.class));
 				}
 
 			} catch (Exception ignored) {
@@ -132,13 +133,13 @@ public class SwaggerProvider {
 		paths.forEach(it -> parameters.add(SwaggerEntity.createParameter(it.isRequired(), it.getName(), VAR_NAME_PATH_VARIABLE, it.getDataType().getJavascriptType(), it.getDescription(), it.getValue())));
 		try {
 			BaseDefinition baseDefinition = info.getRequestBodyDefinition();
-			if (baseDefinition!= null && baseDefinition.getChildren().size() > 0) {
-                Map<String, Object> parameter = SwaggerEntity.createParameter(baseDefinition.isRequired(), StringUtils.isNotBlank(baseDefinition.getName()) ? baseDefinition.getName() : VAR_NAME_REQUEST_BODY, VAR_NAME_REQUEST_BODY, baseDefinition.getDataType().getJavascriptType(), baseDefinition.getDescription(), baseDefinition);
+			if (baseDefinition != null && !CollectionUtils.isEmpty(baseDefinition.getChildren())) {
+				Map<String, Object> parameter = SwaggerEntity.createParameter(baseDefinition.isRequired(), StringUtils.isNotBlank(baseDefinition.getName()) ? baseDefinition.getName() : VAR_NAME_REQUEST_BODY, VAR_NAME_REQUEST_BODY, baseDefinition.getDataType().getJavascriptType(), baseDefinition.getDescription(), baseDefinition);
 				Map<String, Object> schema = new HashMap<>(2);
 				String groupName = groupServiceProvider.getFullName(info.getGroupId()).replace("/", "-");
-				String voName =  groupName + "«" + info.getPath().replaceFirst("/", "").replaceAll("/", "_") + "«request«";
-				if (VAR_NAME_REQUEST_BODY_VALUE_TYPE_ARRAY.equalsIgnoreCase(baseDefinition.getDataType().getJavascriptType())) {
-					voName += "root_" + (StringUtils.isNotBlank(baseDefinition.getName()) ? baseDefinition.getName() + "_" : "_") +  "»»»";
+				String voName = groupName + "«" + info.getPath().replaceFirst("/", "").replaceAll("/", "_") + "«request«";
+				if (DataType.Array == baseDefinition.getDataType()) {
+					voName += "root_" + (StringUtils.isNotBlank(baseDefinition.getName()) ? baseDefinition.getName() + "_" : "_") + "»»»";
 
 					Map<String, Object> items = new HashMap<>(2);
 					items.put("originalRef", voName);
@@ -152,7 +153,7 @@ public class SwaggerProvider {
 				}
 				parameter.put("schema", schema);
 				parameters.add(parameter);
-			}else{
+			} else {
 				Object object = mapper.readValue(info.getRequestBody(), Object.class);
 				if ((object instanceof List || object instanceof Map) && BooleanLiteral.isTrue(object)) {
 					parameters.add(SwaggerEntity.createParameter(false, VAR_NAME_REQUEST_BODY, VAR_NAME_REQUEST_BODY, object instanceof List ? VAR_NAME_REQUEST_BODY_VALUE_TYPE_ARRAY : VAR_NAME_REQUEST_BODY_VALUE_TYPE_OBJECT, null, object));
@@ -168,9 +169,9 @@ public class SwaggerProvider {
 		Map<String, Object> result = new HashMap<>();
 
 		BaseDefinition baseDefinition = info.getResponseBodyDefinition();
-		if (baseDefinition.getChildren().size() > 0) {
+		if (!CollectionUtils.isEmpty(baseDefinition.getChildren())) {
 			String groupName = groupServiceProvider.getFullName(info.getGroupId()).replace("/", "-");
-			String voName =  groupName + "«" + info.getPath().replaceFirst("/", "").replaceAll("/", "_") + "«response«";
+			String voName = groupName + "«" + info.getPath().replaceFirst("/", "").replaceAll("/", "_") + "«response«";
 			voName += "root_" + baseDefinition.getName() + "»»»";
 
 			Map<String, Object> schema = new HashMap<>(2);
@@ -185,19 +186,20 @@ public class SwaggerProvider {
 
 		return result;
 	}
-    private Map<String, Object> doProcessDefinition(BaseDefinition target, ApiInfo info, String parentName, String definitionType, int level) {
-        Map<String, Object> result = new HashMap<>(3);
-        result.put("description", target.getDescription());
-        if (VAR_NAME_REQUEST_BODY_VALUE_TYPE_ARRAY.equalsIgnoreCase(target.getDataType().getJavascriptType())) {
-            if (target.getChildren().size() > 0) {
-                result.put("items", doProcessDefinition(target.getChildren().get(0), info, parentName + target.getName() + "_", definitionType, level + 1));
-            } else {
-                result.put("items", Collections.emptyList());
-            }
+
+	private Map<String, Object> doProcessDefinition(BaseDefinition target, ApiInfo info, String parentName, String definitionType, int level) {
+		Map<String, Object> result = new HashMap<>(3);
+		result.put("description", target.getDescription());
+		if (DataType.Array == target.getDataType()) {
+			if (!CollectionUtils.isEmpty(target.getChildren())) {
+				result.put("items", doProcessDefinition(target.getChildren().get(0), info, parentName + target.getName() + "_", definitionType, level + 1));
+			} else {
+				result.put("items", Collections.emptyList());
+			}
 			result.put("type", target.getDataType().getJavascriptType());
-        } else if (VAR_NAME_REQUEST_BODY_VALUE_TYPE_OBJECT.equalsIgnoreCase(target.getDataType().getJavascriptType())) {
-            String groupName = groupServiceProvider.getFullName(info.getGroupId()).replace("/", "-");
-            String voName = groupName + "«" + info.getPath().replaceFirst("/", "").replaceAll("/", "_") + (StringUtils.equals("response", definitionType) ? "«response«" : "«request«") + parentName + target.getName()  + "»»»";
+		} else if (DataType.Object == target.getDataType()) {
+			String groupName = groupServiceProvider.getFullName(info.getGroupId()).replace("/", "-");
+			String voName = groupName + "«" + info.getPath().replaceFirst("/", "").replaceAll("/", "_") + (StringUtils.equals("response", definitionType) ? "«response«" : "«request«") + parentName + target.getName() + "»»»";
 
 			Map<String, Object> definition = new HashMap<>(3);
 			Map<String, Map<String, Object>> properties = new HashMap<>(target.getChildren().size());
@@ -206,21 +208,21 @@ public class SwaggerProvider {
 			}
 			definition.put("properties", properties);
 			definition.put("description", target.getDescription());
-			definition.put("type", target.getDataType());
+			definition.put("type", target.getDataType().getJavascriptType());
 
-            if (this.DEFINITION_MAP.containsKey(voName)) {
+			if (this.DEFINITION_MAP.containsKey(voName)) {
 				// TODO 应该不会出现名字都一样的
 				voName = voName.replace("»»»", "_" + level + "»»»");
 			}
 
 			this.DEFINITION_MAP.put(voName, definition);
-            result.put("originalRef", voName);
-            result.put("$ref", DEFINITION + voName);
-
-        } else {
-            result.put("example", target.getValue());
-			result.put("type", target.getDataType());
-        }
-        return result;
-    }
+			result.put("originalRef", voName);
+			result.put("$ref", DEFINITION + voName);
+
+		} else {
+			result.put("example", target.getValue());
+			result.put("type", target.getDataType().getJavascriptType());
+		}
+		return result;
+	}
 }

+ 39 - 0
magic-api/src/main/java/org/ssssssss/magicapi/utils/Invoker.java

@@ -0,0 +1,39 @@
+package org.ssssssss.magicapi.utils;
+
+import org.ssssssss.script.reflection.JavaInvoker;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+/**
+ * TODO 此类兼容ASM分支处理,待切换至ASM分支时删除。
+ */
+public class Invoker {
+
+	private final JavaInvoker<?> target;
+
+	private static final Method invokeMethod;
+
+	static {
+		invokeMethod = Arrays.stream(JavaInvoker.class.getDeclaredMethods())
+				.filter(it -> "invoke0".equals(it.getName()))
+				.findFirst()
+				.orElse(null);
+	}
+
+	private Invoker(JavaInvoker<?> target) {
+		this.target = target;
+	}
+
+	public Class<?>[] getParameterTypes(){
+		return this.target.getParameterTypes();
+	}
+
+	public static Invoker from(JavaInvoker<?> target) {
+		return target == null ? null : new Invoker(target);
+	}
+
+	public Object invoke(Object target, Object context, Object ... args) throws Throwable{
+		return invokeMethod.invoke(this.target, target, context, args);
+	}
+}