Эх сурвалжийг харах

解决与应用接口路径冲突的问题

mxd 4 жил өмнө
parent
commit
f0f2fde48c

+ 225 - 204
src/main/java/org/ssssssss/magicapi/config/MappingHandlerMapping.java

@@ -7,6 +7,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.context.request.NativeWebRequest;
 import org.springframework.web.context.request.RequestAttributes;
 import org.springframework.web.context.request.ServletWebRequest;
+import org.springframework.web.method.HandlerMethod;
 import org.springframework.web.servlet.HandlerMapping;
 import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
 import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
@@ -15,10 +16,7 @@ import org.ssssssss.magicapi.provider.ApiServiceProvider;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
@@ -26,205 +24,228 @@ import java.util.concurrent.ConcurrentHashMap;
  */
 public class MappingHandlerMapping {
 
-    /**
-     * 已缓存的映射信息
-     */
-    private static Map<String, ApiInfo> mappings = new ConcurrentHashMap<>();
-
-    private static Logger logger = LoggerFactory.getLogger(MappingHandlerMapping.class);
-
-    /**
-     * spring中的请求映射处理器
-     */
-    private RequestMappingHandlerMapping requestMappingHandlerMapping;
-
-    /**
-     * 请求处理器
-     */
-    private RequestHandler handler;
-
-    /**
-     * 请求到达时处理的方法
-     */
-    private Method method = RequestHandler.class.getDeclaredMethod("invoke", HttpServletRequest.class, HttpServletResponse.class, Map.class, Map.class);
-
-    /**
-     * 接口信息读取
-     */
-    private ApiServiceProvider magicApiService;
-    /**
-     * 统一接口前缀
-     */
-    private String prefix;
-
-    /**
-     * 缓存已映射的接口信息
-     */
-    private List<ApiInfo> apiInfos = Collections.synchronizedList(new ArrayList<>());
-
-    public MappingHandlerMapping() throws NoSuchMethodException {
-    }
-
-    public void setPrefix(String prefix) {
-        this.prefix = prefix;
-    }
-
-    /**
-     * 根据request获取对应的接口信息
-     */
-    public static ApiInfo getMappingApiInfo(HttpServletRequest request) {
-        NativeWebRequest webRequest = new ServletWebRequest(request);
-        // 找到注册的路径
-        String requestMapping = (String) webRequest.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
-        // 根据请求方法和路径获取接口信息
-        return getMappingApiInfo(buildMappingKey(request.getMethod(), requestMapping));
-    }
-
-    /**
-     * 根据绑定的key获取接口信息
-     */
-    public static ApiInfo getMappingApiInfo(String key) {
-        return mappings.get(key);
-    }
-
-    /**
-     * 构建缓存map的key
-     *
-     * @param requestMethod  请求方法
-     * @param requestMapping 请求路径
-     * @return
-     */
-    public static String buildMappingKey(String requestMethod, String requestMapping) {
-        //TODO 判断 requestMapping 是否已 “/” 开头
-        if (!StringUtils.isEmpty(requestMapping) && !requestMapping.startsWith("/")) {
-            requestMapping = "/" + requestMapping;
-        }
-        return requestMethod.toUpperCase() + ":" + requestMapping;
-    }
-
-    public void setRequestMappingHandlerMapping(RequestMappingHandlerMapping requestMappingHandlerMapping) {
-        this.requestMappingHandlerMapping = requestMappingHandlerMapping;
-    }
-
-    public void setHandler(RequestHandler handler) {
-        this.handler = handler;
-    }
-
-    public void setMagicApiService(ApiServiceProvider magicApiService) {
-        this.magicApiService = magicApiService;
-    }
-
-    public List<ApiInfo> getApiInfos() {
-        return apiInfos;
-    }
-
-    /**
-     * 注册请求
-     */
-    public void registerAllMapping() {
-        List<ApiInfo> list = magicApiService.listWithScript();
-        if (list != null) {
-            apiInfos.addAll(list);
-            for (ApiInfo info : list) {
-                registerMapping(info, false);
-            }
-        }
-    }
-
-    /**
-     * 根据请求方法和路径获取接口信息
-     *
-     * @param method         请求方法
-     * @param requestMapping 请求路径
-     */
-    public ApiInfo getApiInfo(String method, String requestMapping) {
-        return mappings.get(buildMappingKey(method, requestMapping));
-    }
-
-    public void updateGroupPrefix(String oldGroupName, String newGroupName, String prefix) {
-        for (ApiInfo info : apiInfos) {
-            if (oldGroupName.equals(info.getGroupName())) {
-                unregisterMapping(info.getId(), false);
-                info.setGroupName(newGroupName);
-                info.setGroupPrefix(prefix);
-                registerMapping(info, false);
-            }
-        }
-    }
-
-    /**
-     * 注册请求映射
-     *
-     */
-    public void registerMapping(ApiInfo info, boolean delete) {
-        // 先判断是否已注册,如果已注册,则先取消注册在进行注册。
-        if (mappings.containsKey(info.getId())) {
-            ApiInfo oldInfo = mappings.get(info.getId());
-            logger.info("取消注册接口:{}", oldInfo.getName());
-            // 取消注册
-            mappings.remove(getMappingKey(info));
-            requestMappingHandlerMapping.unregisterMapping(getRequestMapping(oldInfo));
-        }
-        logger.info("注册接口:{}", info.getName());
-        // 注册
-        RequestMappingInfo requestMapping = getRequestMapping(info);
-        mappings.put(info.getId(), info);
-        mappings.put(getMappingKey(info), info);
-        requestMappingHandlerMapping.registerMapping(requestMapping, handler, method);
-        if (delete) {   // 刷新缓存
-            apiInfos.removeIf(i -> i.getId().equalsIgnoreCase(info.getId()));
-            apiInfos.add(info);
-        }
-    }
-
-    /**
-     * 取消注册请求映射
-     */
-    public void unregisterMapping(String id, boolean delete) {
-        ApiInfo info = mappings.remove(id);
-        if (info != null) {
-            logger.info("取消注册接口:{}", info.getName());
-            mappings.remove(getMappingKey(info));
-            requestMappingHandlerMapping.unregisterMapping(getRequestMapping(info));
-            if (delete) {   //刷新缓存
-                apiInfos.removeIf(i -> i.getId().equalsIgnoreCase(info.getId()));
-            }
-        }
-    }
-
-    /**
-     * 根据接口信息获取绑定map的key
-     */
-    private String getMappingKey(ApiInfo info) {
-        return buildMappingKey(info.getMethod(), getRequestPath(info.getGroupPrefix(), info.getPath()));
-    }
-
-    /**
-     * 处理前缀
-     *
-     * @param groupPrefix 分组前缀
-     * @param path 请求路径
-     */
-    public String getRequestPath(String groupPrefix, String path) {
-        groupPrefix = groupPrefix == null ? "" : groupPrefix;
-        while (groupPrefix.endsWith("/")) {
-            groupPrefix = groupPrefix.substring(0, groupPrefix.length() - 1);
-        }
-        while (path.startsWith("/")) {
-            path = path.substring(1);
-        }
-        path = groupPrefix + "/" + path;
-        if (prefix != null) {
-            path = prefix + (path.startsWith("/") ? path.substring(1) : path);
-        }
-        return path;
-    }
-
-    /**
-     * 根据接口信息构建 RequestMappingInfo
-     */
-    private RequestMappingInfo getRequestMapping(ApiInfo info) {
-        return RequestMappingInfo.paths(getRequestPath(info.getGroupPrefix(), info.getPath())).methods(RequestMethod.valueOf(info.getMethod().toUpperCase())).build();
-    }
+	/**
+	 * 已缓存的映射信息
+	 */
+	private static Map<String, ApiInfo> mappings = new ConcurrentHashMap<>();
+
+	private static Logger logger = LoggerFactory.getLogger(MappingHandlerMapping.class);
+
+	/**
+	 * spring中的请求映射处理器
+	 */
+	private RequestMappingHandlerMapping requestMappingHandlerMapping;
+
+	/**
+	 * 请求处理器
+	 */
+	private RequestHandler handler;
+
+	/**
+	 * 请求到达时处理的方法
+	 */
+	private Method method = RequestHandler.class.getDeclaredMethod("invoke", HttpServletRequest.class, HttpServletResponse.class, Map.class, Map.class);
+
+	/**
+	 * 接口信息读取
+	 */
+	private ApiServiceProvider magicApiService;
+	/**
+	 * 统一接口前缀
+	 */
+	private String prefix;
+
+	/**
+	 * 缓存已映射的接口信息
+	 */
+	private List<ApiInfo> apiInfos = Collections.synchronizedList(new ArrayList<>());
+
+	public MappingHandlerMapping() throws NoSuchMethodException {
+	}
+
+	public void setPrefix(String prefix) {
+		this.prefix = prefix;
+	}
+
+	/**
+	 * 根据request获取对应的接口信息
+	 */
+	public static ApiInfo getMappingApiInfo(HttpServletRequest request) {
+		NativeWebRequest webRequest = new ServletWebRequest(request);
+		// 找到注册的路径
+		String requestMapping = (String) webRequest.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
+		// 根据请求方法和路径获取接口信息
+		return getMappingApiInfo(buildMappingKey(request.getMethod(), requestMapping));
+	}
+
+	/**
+	 * 根据绑定的key获取接口信息
+	 */
+	public static ApiInfo getMappingApiInfo(String key) {
+		return mappings.get(key);
+	}
+
+	/**
+	 * 构建缓存map的key
+	 *
+	 * @param requestMethod  请求方法
+	 * @param requestMapping 请求路径
+	 * @return
+	 */
+	public static String buildMappingKey(String requestMethod, String requestMapping) {
+		//TODO 判断 requestMapping 是否已 “/” 开头
+		if (!StringUtils.isEmpty(requestMapping) && !requestMapping.startsWith("/")) {
+			requestMapping = "/" + requestMapping;
+		}
+		return requestMethod.toUpperCase() + ":" + requestMapping;
+	}
+
+	public void setRequestMappingHandlerMapping(RequestMappingHandlerMapping requestMappingHandlerMapping) {
+		this.requestMappingHandlerMapping = requestMappingHandlerMapping;
+	}
+
+	public void setHandler(RequestHandler handler) {
+		this.handler = handler;
+	}
+
+	public void setMagicApiService(ApiServiceProvider magicApiService) {
+		this.magicApiService = magicApiService;
+	}
+
+	public List<ApiInfo> getApiInfos() {
+		return apiInfos;
+	}
+
+	/**
+	 * 注册请求
+	 */
+	public void registerAllMapping() {
+		List<ApiInfo> list = magicApiService.listWithScript();
+		if (list != null) {
+			apiInfos.addAll(list);
+			for (ApiInfo info : list) {
+				try {
+					registerMapping(info, false);
+				} catch (Exception e) {
+					logger.error("接口:{}注册失败", info.getName(), e);
+				}
+			}
+		}
+	}
+
+	/**
+	 * 根据请求方法和路径获取接口信息
+	 *
+	 * @param method         请求方法
+	 * @param requestMapping 请求路径
+	 */
+	public ApiInfo getApiInfo(String method, String requestMapping) {
+		return mappings.get(buildMappingKey(method, requestMapping));
+	}
+
+	public void updateGroupPrefix(String oldGroupName, String newGroupName, String prefix) {
+		for (ApiInfo info : apiInfos) {
+			if (oldGroupName.equals(info.getGroupName())) {
+				unregisterMapping(info.getId(), false);
+				info.setGroupName(newGroupName);
+				info.setGroupPrefix(prefix);
+				registerMapping(info, false);
+			}
+		}
+	}
+
+	/**
+	 * 判断是否已注册
+	 */
+	public boolean hasRegisterMapping(ApiInfo info) {
+		if (info.getId() != null) {
+			ApiInfo oldInfo = mappings.get(info.getId());
+			if (oldInfo != null
+					&& Objects.equals(oldInfo.getGroupPrefix(), info.getGroupPrefix())
+					&& Objects.equals(oldInfo.getMethod(), info.getMethod())
+					&& Objects.equals(oldInfo.getPath(), info.getPath())) {
+				return false;
+			}
+		}
+		Map<RequestMappingInfo, HandlerMethod> handlerMethods = this.requestMappingHandlerMapping.getHandlerMethods();
+		if (handlerMethods != null) {
+			return handlerMethods.get(getRequestMapping(info)) != null;
+		}
+		return false;
+	}
+
+	/**
+	 * 注册请求映射
+	 */
+	public void registerMapping(ApiInfo info, boolean delete) {
+		// 先判断是否已注册,如果已注册,则先取消注册在进行注册。
+		if (mappings.containsKey(info.getId())) {
+			ApiInfo oldInfo = mappings.get(info.getId());
+			logger.info("取消注册接口:{}", oldInfo.getName());
+			// 取消注册
+			mappings.remove(getMappingKey(info));
+			requestMappingHandlerMapping.unregisterMapping(getRequestMapping(oldInfo));
+		}
+		logger.info("注册接口:{}", info.getName());
+		// 注册
+		RequestMappingInfo requestMapping = getRequestMapping(info);
+		mappings.put(info.getId(), info);
+		mappings.put(getMappingKey(info), info);
+		requestMappingHandlerMapping.registerMapping(requestMapping, handler, method);
+		if (delete) {   // 刷新缓存
+			apiInfos.removeIf(i -> i.getId().equalsIgnoreCase(info.getId()));
+			apiInfos.add(info);
+		}
+	}
+
+	/**
+	 * 取消注册请求映射
+	 */
+	public void unregisterMapping(String id, boolean delete) {
+		ApiInfo info = mappings.remove(id);
+		if (info != null) {
+			logger.info("取消注册接口:{}", info.getName());
+			mappings.remove(getMappingKey(info));
+			requestMappingHandlerMapping.unregisterMapping(getRequestMapping(info));
+			if (delete) {   //刷新缓存
+				apiInfos.removeIf(i -> i.getId().equalsIgnoreCase(info.getId()));
+			}
+		}
+	}
+
+	/**
+	 * 根据接口信息获取绑定map的key
+	 */
+	private String getMappingKey(ApiInfo info) {
+		return buildMappingKey(info.getMethod(), getRequestPath(info.getGroupPrefix(), info.getPath()));
+	}
+
+	/**
+	 * 处理前缀
+	 *
+	 * @param groupPrefix 分组前缀
+	 * @param path        请求路径
+	 */
+	public String getRequestPath(String groupPrefix, String path) {
+		groupPrefix = groupPrefix == null ? "" : groupPrefix;
+		while (groupPrefix.endsWith("/")) {
+			groupPrefix = groupPrefix.substring(0, groupPrefix.length() - 1);
+		}
+		while (path.startsWith("/")) {
+			path = path.substring(1);
+		}
+		path = groupPrefix + "/" + path;
+		if (prefix != null) {
+			path = prefix + (path.startsWith("/") ? path.substring(1) : path);
+		}
+		return path;
+	}
+
+	/**
+	 * 根据接口信息构建 RequestMappingInfo
+	 */
+	private RequestMappingInfo getRequestMapping(ApiInfo info) {
+		return RequestMappingInfo.paths(getRequestPath(info.getGroupPrefix(), info.getPath())).methods(RequestMethod.valueOf(info.getMethod().toUpperCase())).build();
+	}
 
 }

+ 3 - 0
src/main/java/org/ssssssss/magicapi/config/WebUIController.java

@@ -452,6 +452,9 @@ public class WebUIController {
 			if (StringUtils.isBlank(info.getScript())) {
 				return new JsonBean<>(0, "脚本内容不能为空");
 			}
+			if(mappingHandlerMapping.hasRegisterMapping(info)){
+				return new JsonBean<>(0, "该路径已被映射,请换一个请求方法或路径");
+			}
 			if (StringUtils.isBlank(info.getId())) {
 				// 先判断接口是否存在
 				if (magicApiService.exists(info.getGroupPrefix(), info.getMethod(), info.getPath())) {