All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ssssssss.magicapi.config.MappingHandlerMapping Maven / Gradle / Ivy

package org.ssssssss.magicapi.config;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
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;
import org.ssssssss.magicapi.controller.RequestHandler;
import org.ssssssss.magicapi.model.ApiInfo;
import org.ssssssss.magicapi.model.Group;
import org.ssssssss.magicapi.model.TreeNode;
import org.ssssssss.magicapi.provider.ApiServiceProvider;
import org.ssssssss.magicapi.provider.GroupServiceProvider;
import org.ssssssss.magicapi.utils.Mapping;
import org.ssssssss.magicapi.utils.PathUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 请求映射
 */
public class MappingHandlerMapping {

	/**
	 * 已缓存的映射信息
	 */
	private static final Map mappings = new ConcurrentHashMap<>();

	private static final Logger logger = LoggerFactory.getLogger(MappingHandlerMapping.class);
	/**
	 * 接口分组
	 */
	private static TreeNode groups;
	/**
	 * 请求到达时处理的方法
	 */
	private final Method method = RequestHandler.class.getDeclaredMethod("invoke", HttpServletRequest.class, HttpServletResponse.class, Map.class, Map.class);
	/**
	 * 统一接口前缀
	 */
	private final String prefix;
	/**
	 * 是否覆盖应用接口
	 */
	private final boolean allowOverride;
	/**
	 * 缓存已映射的接口信息
	 */
	private final List apiInfos = Collections.synchronizedList(new ArrayList<>());

	private Mapping mappingHelper;
	/**
	 * 请求处理器
	 */
	private Object handler;
	/**
	 * 接口信息读取
	 */
	private ApiServiceProvider magicApiService;
	/**
	 * 分组信息读取
	 */
	private GroupServiceProvider groupServiceProvider;

	public MappingHandlerMapping(String prefix, boolean allowOverride) throws NoSuchMethodException {
		this.prefix = prefix;
		this.allowOverride = allowOverride;
	}

	/**
	 * 根据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获取接口信息
	 */
	private static ApiInfo getMappingApiInfo(String key) {
		return mappings.get(key).getInfo();
	}

	/**
	 * 构建缓存map的key
	 *
	 * @param requestMethod  请求方法
	 * @param requestMapping 请求路径
	 */
	private static String buildMappingKey(String requestMethod, String requestMapping) {
		if (StringUtils.isNotBlank(requestMapping) && !requestMapping.startsWith("/")) {
			requestMapping = "/" + requestMapping;
		}
		return Objects.toString(requestMethod, "GET").toUpperCase() + ":" + requestMapping;
	}

	public static Group findGroup(String groupId) {
		TreeNode node = groups.findTreeNode(it -> it.getId().equals(groupId));
		return node != null ? node.getNode() : null;
	}

	public static List findGroups(String groupId) {
		List groups = new ArrayList<>();
		Group group;
		while (!"0".equals(groupId) && (group = MappingHandlerMapping.findGroup(groupId)) != null) {
			groups.add(group);
			groupId = group.getParentId();
		}
		return groups;
	}

	public TreeNode findGroupTree(String groupId){
		return groups.findTreeNode(it -> it.getId().equals(groupId));
	}

	public void setRequestMappingHandlerMapping(RequestMappingHandlerMapping requestMappingHandlerMapping) {
		this.mappingHelper = Mapping.create(requestMappingHandlerMapping);
	}

	public void setHandler(Object handler) {
		this.handler = handler;
	}

	public void setMagicApiService(ApiServiceProvider magicApiService) {
		this.magicApiService = magicApiService;
	}

	public void setGroupServiceProvider(GroupServiceProvider groupServiceProvider) {
		this.groupServiceProvider = groupServiceProvider;
	}

	public List getApiInfos() {
		return apiInfos;
	}

	/**
	 * 加载所有分组
	 */
	public synchronized void loadGroup() {
		groups = groupServiceProvider.apiGroupTree();
	}

	/**
	 * 注册请求
	 */
	public void registerAllMapping() {
		try {
			loadGroup();
			List list = magicApiService.listWithScript();
			if (list != null) {
				list = list.stream().filter(it -> groupServiceProvider.getFullPath(it.getGroupId()) != null).collect(Collectors.toList());
				for (ApiInfo info : list) {
					try {
						// 当接口存在时,刷新缓存
						registerMapping(info, true);
					} catch (Exception e) {
						logger.error("接口:{}注册失败", info.getName(), e);
					}
				}
				List resistedList = list.stream().map(ApiInfo::getId).collect(Collectors.toList());
				Iterator iterator = apiInfos.iterator();
				while (iterator.hasNext()) {
					String oldId = iterator.next().getId();
					// 当接口不存在时,取消注册接口
					if (!resistedList.contains(oldId)) {
						unregisterMapping(oldId, false);
						iterator.remove();
					}
				}
			}
		} catch (Exception e) {
			logger.info("注册接口映射失败", e);
		}
	}

	/**
	 * 根据请求方法和路径获取接口信息
	 *
	 * @param method         请求方法
	 * @param requestMapping 请求路径
	 */
	public ApiInfo getApiInfo(String method, String requestMapping) {
		MappingNode mappingNode = mappings.get(buildMappingKey(method, concatPath("", requestMapping)));
		return mappingNode == null ? null : mappingNode.getInfo();
	}

	private boolean hasConflict(TreeNode group, String newPath) {
		// 获取要移动的接口
		List infos = apiInfos.stream().filter(info -> Objects.equals(info.getGroupId(), group.getNode().getId())).collect(Collectors.toList());
		// 判断是否有冲突
		for (ApiInfo info : infos) {
			String path = concatPath(newPath, "/" + info.getPath());
			String mappingKey = buildMappingKey(info.getMethod(), path);
			MappingNode mappingNode = mappings.get(mappingKey);
			if (mappingNode != null) {
				if (mappingNode.getInfo().equals(info)) {
					continue;
				}
				return true;
			}
			if (!allowOverride) {
				Map handlerMethods = this.mappingHelper.getHandlerMethods();
				if (handlerMethods.get(getRequestMapping(info.getMethod(), path)) != null) {
					return true;
				}
			}
		}
		for (TreeNode child : group.getChildren()) {
			if (hasConflict(child, newPath + "/" + Objects.toString(child.getNode().getPath(), ""))) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 检测是否允许修改
	 */
	public boolean checkGroup(Group group) {
		TreeNode oldTree = groups.findTreeNode((item) -> item.getId().equals(group.getId()));
		// 如果没移动目录且没改路径,则只需要判断名字是否冲突
		boolean parentIdEquals = Objects.equals(oldTree.getNode().getParentId(), group.getParentId());
		boolean nameEquals = Objects.equals(oldTree.getNode().getName(), group.getName());
		if (parentIdEquals && Objects.equals(oldTree.getNode().getPath(), group.getPath())) {
			return nameEquals || !groupServiceProvider.exists(group);
		}
		// 检测名字是否冲突
		if ((!parentIdEquals || !nameEquals) && groupServiceProvider.exists(group)) {
			return false;
		}
		// 新的接口分组路径
		String newPath = groupServiceProvider.getFullPath(group.getParentId());
		// 检测冲突
		return !hasConflict(oldTree, newPath + "/" + Objects.toString(group.getPath(), ""));
	}

	public boolean hasRegister(Set paths) {
		return paths.stream().anyMatch(mappings::containsKey);
	}

	/**
	 * 删除分组
	 */
	public void deleteGroup(List groupIds) {
		// 找到对应的所有接口
		List deleteInfos = apiInfos.stream().filter(info -> groupIds.contains(info.getGroupId())).collect(Collectors.toList());
		for (ApiInfo info : deleteInfos) {
			unregisterMapping(info.getId(), true);
		}
		// 全部删除
		apiInfos.removeAll(deleteInfos);
		// 刷新分组缓存
		loadGroup();
	}

	/**
	 * 修改分组
	 */
	public boolean updateGroup(String groupId) {
		loadGroup();    // 重新加载分组
		TreeNode groupTreeNode = groups.findTreeNode((item) -> item.getId().equals(groupId));
		recurseUpdateGroup(groupTreeNode, true);
		return magicApiService.reload(groupId);
	}

	private void recurseUpdateGroup(TreeNode node, boolean updateGroupId) {
		apiInfos.stream().filter(info -> Objects.equals(info.getGroupId(), node.getNode().getId())).forEach(info -> {
			unregisterMapping(info.getId(), false);
			if (updateGroupId) {
				info.setGroupId(node.getNode().getId());
			}
			registerMapping(info, false);
		});
		for (TreeNode child : node.getChildren()) {
			recurseUpdateGroup(child, false);
		}
	}

	/**
	 * 判断是否已注册
	 */
	public boolean hasRegisterMapping(ApiInfo info) {
		if (info.getId() != null) {
			MappingNode mappingNode = mappings.get(info.getId());
			ApiInfo oldInfo = mappingNode == null ? null : mappingNode.getInfo();
			if (oldInfo != null
					&& Objects.equals(oldInfo.getGroupId(), info.getGroupId())
					&& Objects.equals(oldInfo.getMethod(), info.getMethod())
					&& Objects.equals(oldInfo.getPath(), info.getPath())) {
				return false;
			}
		}
		String mappingKey = getMappingKey(info);
		if (mappings.containsKey(mappingKey)) {
			return !mappings.get(mappingKey).getInfo().getId().equals(info.getId());
		}
		if (!allowOverride) {
			Map handlerMethods = this.mappingHelper.getHandlerMethods();
			return handlerMethods.get(getRequestMapping(info)) != null;
		}
		return false;
	}

	/**
	 * 接口移动
	 */
	public boolean move(String id, String groupId) {
		MappingNode mappingNode = mappings.get(id);
		if (mappingNode == null) {
			return false;
		}
		ApiInfo copy = mappingNode.getInfo().copy();
		copy.setGroupId(groupId);
		if (hasRegisterMapping(copy)) {
			return false;
		}
		unregisterMapping(id, true);
		registerMapping(copy, true);
		return true;
	}

	/**
	 * 注册请求映射
	 */
	public void registerMapping(ApiInfo info, boolean delete) {
		if(info == null){
			return;
		}
		// 先判断是否已注册,如果已注册,则先取消注册在进行注册。
		MappingNode mappingNode = mappings.get(info.getId());
		String newMappingKey = getMappingKey(info);
		if (mappingNode != null) {
			ApiInfo oldInfo = mappingNode.getInfo();
			String oldMappingKey = mappingNode.getMappingKey();
			// URL 路径一致时,刷新脚本内容即可
			if (Objects.equals(oldMappingKey, newMappingKey)) {
				if (!info.equals(oldInfo)) {
					mappingNode.setInfo(info);
					mappings.get(newMappingKey).setInfo(info);
					if (delete) {
						refreshCache(info);
					}
					logger.info("刷新接口:{},{}", info.getName(), newMappingKey);
				}
				return;
			}
			// URL不一致时,需要取消注册旧接口,重新注册新接口
			logger.info("取消注册接口:{},{}", oldInfo.getName(), oldMappingKey);
			// 取消注册
			mappings.remove(oldMappingKey);
			mappingHelper.unregister(getRequestMapping(oldInfo));
		}
		mappingNode = new MappingNode(info);
		mappingNode.setMappingKey(newMappingKey);
		// 注册
		RequestMappingInfo requestMapping = getRequestMapping(info);
		mappingNode.setRequestMappingInfo(requestMapping);
		mappingNode.setInfo(info);
		// 如果与应用冲突
		if (!overrideApplicationMapping(requestMapping)) {
			logger.error("接口{},{}与应用冲突,无法注册", info.getName(), newMappingKey);
			return;
		}
		logger.info("注册接口:{},{}", info.getName(), newMappingKey);
		mappings.put(info.getId(), mappingNode);
		mappings.put(newMappingKey, mappingNode);
		registerMapping(requestMapping, handler, method);
		if (delete) {   // 刷新缓存
			refreshCache(info);
		}
	}

	private void refreshCache(ApiInfo info) {
		apiInfos.removeIf(i -> i.getId().equalsIgnoreCase(info.getId()));
		apiInfos.add(info);
	}

	private void registerMapping(RequestMappingInfo requestMapping, Object handler, Method method) {
		mappingHelper.register(requestMapping, handler, method);
	}

	/**
	 * 取消注册请求映射
	 */
	public void unregisterMapping(String id, boolean delete) {
		MappingNode mappingNode = mappings.remove(id);
		if (mappingNode != null) {
			ApiInfo info = mappingNode.getInfo();
			logger.info("取消注册接口:{}", info.getName());
			mappings.remove(mappingNode.getMappingKey());
			mappingHelper.unregister(mappingNode.getRequestMappingInfo());
			if (delete) {   //刷新缓存
				apiInfos.removeIf(i -> i.getId().equalsIgnoreCase(info.getId()));
			}
		}
	}

	/**
	 * 根据接口信息获取绑定map的key
	 */
	private String getMappingKey(ApiInfo info) {
		return buildMappingKey(info.getMethod(), getRequestPath(info.getGroupId(), info.getPath()));
	}

	/**
	 * 处理前缀
	 *
	 * @param groupId 分组ID
	 * @param path    请求路径
	 */
	public String getRequestPath(String groupId, String path) {
		return concatPath(groupServiceProvider.getFullPath(groupId), path);

	}

	public void registerController(Object target, String base) {
		Method[] methods = target.getClass().getDeclaredMethods();
		for (Method method : methods) {
			RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
			if (requestMapping != null) {
				String[] paths = Stream.of(requestMapping.value()).map(value -> base + value).toArray(String[]::new);
				mappingHelper.register(RequestMappingInfo.paths(paths).build(), target, method);
			}
		}
	}

	private String concatPath(String groupPath, String path) {
		path = groupPath + "/" + path;
		if (prefix != null) {
			path = prefix + "/" + path;
		}
		path = PathUtils.replaceSlash(path);
		if (path.startsWith("/")) {
			return path.substring(1);
		}
		return path;
	}

	/**
	 * 覆盖应用接口
	 */
	private boolean overrideApplicationMapping(RequestMappingInfo requestMapping) {
		if (mappingHelper.getHandlerMethods().containsKey(requestMapping)) {
			if (!allowOverride) {
				// 不允许覆盖
				return false;
			}
			logger.warn("取消注册应用接口:{}", requestMapping);
			// 取消注册原接口
			mappingHelper.unregister(requestMapping);
		}
		return true;
	}

	/**
	 * 根据接口信息构建 RequestMappingInfo
	 */
	private RequestMappingInfo getRequestMapping(ApiInfo info) {
		return RequestMappingInfo.paths(getRequestPath(info.getGroupId(), info.getPath())).methods(RequestMethod.valueOf(info.getMethod().toUpperCase())).build();
	}

	/**
	 * 根据接口信息构建 RequestMappingInfo
	 */
	private RequestMappingInfo getRequestMapping(String method, String path) {
		return RequestMappingInfo.paths(path).methods(RequestMethod.valueOf(method.toUpperCase())).build();
	}

	static class MappingNode {

		private ApiInfo info;

		private String mappingKey;

		private RequestMappingInfo requestMappingInfo;

		public MappingNode(ApiInfo info) {
			this.info = info;
		}

		public ApiInfo getInfo() {
			return info;
		}

		public void setInfo(ApiInfo info) {
			this.info = info;
		}

		public String getMappingKey() {
			return mappingKey;
		}

		public void setMappingKey(String mappingKey) {
			this.mappingKey = mappingKey;
		}

		public RequestMappingInfo getRequestMappingInfo() {
			return requestMappingInfo;
		}

		public void setRequestMappingInfo(RequestMappingInfo requestMappingInfo) {
			this.requestMappingInfo = requestMappingInfo;
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy