此前,已经完善了Api的功能,但是,目前暂不支持“'get/{id}”此种形式,根据通配符调用接口功能。
一、解析通配符接口配置
- 需求分析,思路梳理
为使api模型支持通配符形式解析,并且后期可以方便扩展其他形式解析,我的思路大致如下:
1、定义一个“api_type”用来存储Api类型,目前仅支持“默认”,“通配符”两种类型。
2、考虑到解析性能问题,定义一个“parse_param_config”字段,用来存储解析值,数据格式大致如下;
[{"index":4,"key":"orderNum"}]
参数Key为映射请求参数对象的名称,后面的数字为以“/”分割后值的“index”位置。
- 代码实现
1、定义Api规则对象
package com.flycoding.drivenlibrary.engine.api.entity.bo;/*** api 规则bo** @author 赵屈犇* @version 1.0* @date 创建时间: 2023/8/3 20:08* @Copyright(C): 2023 by 赵屈犇*/public class ApiMatchBO { /*** 索引位置*/ private int index; /*** 参数主键*/ private String key; public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public String getKey() { return key; } public void setKey(String key) { this.key = key; }}
2、声明Api ORM映射
/*** 服务类型*/@FormFieldConfig(fieldCode = "api_type", fieldName = "服务类型", elementCode = DrivenElementConstants.SELECT_ELEMENT, dict = @DictConfig(dictCode = ApiType.DICTIONARY_CODE), fieldParentName = BASIC_MESSAGE_NAME)@Column(columnName = "api_type", columnType = ColumnType.VARCHAR, length = SqlConstants.DICTIONARY_VALUE_SIZE, isNotNull = true)private String apiType;/*** 解析参数配置*/@Column(columnName = "parse_param_config", columnType = ColumnType.VARCHAR, length = 500)private List<ApiMatchBO> parseParamConfig;
3、声明 apiType 字典数据
/** * api 类型 */@DictionaryConfig(dictionaryCode = ApiType.DICTIONARY_CODE, dictionaryName = "api 类型")public static class ApiType {/*** 字典编码*/ public static final String DICTIONARY_CODE = "1042"; /** * 默认 */ @DictionaryConfig(dictionaryName = "默认") public static final String DEFAULT = "104201"; /** * 通配符 */ @DictionaryConfig(dictionaryName = "通配符") public static final String WILDCARD = "104202";}
4、解析通配符接口
String apiType = ConfigDictionaryConstants.ApiType.DEFAULT;// 分割api 全路径String[] apiFullUrls = (moduleUrl + "/" + api.getApiUrl()).split("/");StringJoiner apiFullUrl = new StringJoiner("/");if (ArrayUtils.isNotEmpty(apiFullUrls)) { JSONObject parseParamConfig = new JSONObject(); for (int i = 0; i < apiFullUrls.length; i++) { String fullUrl = apiFullUrls[i]; if (!fullUrl.equals("/")) { // 解析 {} 通配符 if (RegularHelper.isMatcher(fullUrl, RegularConstants.BRACES)) { Matcher matcher = RegularHelper.getMatcher(fullUrl, RegularConstants.BRACES); while (matcher.find()) { String group = matcher.group(1); parseParamConfig.put(group, i); apiType = ConfigDictionaryConstants.ApiType.WILDCARD; } } else { apiFullUrl.add(fullUrl); }}}api.setParseParamConfig(parseParamConfig);
- 代码执行结果
二、解析支持通配符请求
- 需求分析,思路梳理
此上,已实现结构解析并入库。接下来,我需要实现Api功能支持,通配符访问请求。要实现这种功能,我需要实现Api接口通配符形式的匹配,我有两种实现方式,一种是通过startWith直接判断,另一种是通过正则匹配。
以下是两种实现方式的优劣分析:
- startWith
- 性能高于正则,但是扩展性不好,仅支持get/{id}此种形式
- 正则匹配
- 性能低于startWith,但是扩展性好,可以支持各种形式匹配规则
以下是性能示例图:
可以看出,startWith远远高于正则匹配。综合考虑,我决定采用startWith方式进行匹配。
为提高检索效率,我仅准备缓存通配符类型的Api接口键值对,这样的话,也可以快速匹配索引到对应的Api地址。若未匹配到时,走之前处理逻辑即可。
- 代码实现
1、Api匹配规则工厂
package com.flycoding.drivenlibrary.engine.api.factory;import com.alibaba.fastjson.JSONObject;import com.flycoding.dblibrary.executor.inter.ISqlExecutor;import com.flycoding.drivenlibrary.engine.config.constants.dictionary.ConfigDictionaryConstants;import com.flycoding.drivenlibrary.engine.config.entity.ModelDBConfigMessage;import com.flycoding.drivenlibrary.engine.config.factory.DBConfigFactory;import com.flycoding.drivenlibrary.engine.request.build.sql.QueryBuilder;import com.flycoding.drivenlibrary.enums.dictionary.QueryType;import com.flycoding.utillibrary.java.ArrayUtils;import com.flycoding.utillibrary.logger.LoggerFactory;import java.util.List;/** * api 匹配工厂 * * @author 赵屈犇 * @version 1.0 * @date 创建时间: 2023/8/3 20:26 * @Copyright(C): 2023 by 赵屈犇 */public class ApiMatchFactory { private static final LoggerFactory logger = LoggerFactory.getLogger(ApiMatchFactory.class.getName()); private static ApiMatchFactory instance; /** * DB配置信息的工厂 */ private DBConfigFactory dbConfigFactory; /** * api 匹配地址集合 */ private List<String> apiMatchUrls = ArrayUtils.newArrayList(); public static ApiMatchFactory getInstance() { if (instance == null) { synchronized (ApiMatchFactory.class) { if (instance == null) { instance = new ApiMatchFactory(); } } } return instance; } private ApiMatchFactory() { dbConfigFactory = DBConfigFactory.getInstance(); } /** * 初始化匹配规则 */ public void initMatch() { try { List<ModelDBConfigMessage> dbConfigs = DBConfigFactory.getInstance().getDBConfigs(); if (ArrayUtils.isNotEmpty(dbConfigs)) { for (ModelDBConfigMessage config : dbConfigs) { ISqlExecutor executor = dbConfigFactory.getModelSqlExecutor(config.getDbConfigCode()); List<JSONObject> datas = (List<JSONObject>) QueryBuilder.builder().executor(executor).table("Sy_Api").column("api_full_url", "apiFullUrl") .whereBuilder().and("api_type", QueryType.NOT_EQUAL, ConfigDictionaryConstants.ApiType.DEFAULT) .sqlBuilder().execute(); if (ArrayUtils.isNotEmpty(datas)) { for (JSONObject data : datas) { apiMatchUrls.add(data.getString("apiFullUrl")); } } } } } catch (Exception e) { logger.error("初始化匹配规则报错了,", e); } } /** * 获取api根据规则 * * @param apiUrl * @return */ public String getApiByMatch(String apiUrl) { for (String apiMatchUrl : apiMatchUrls) { if (apiUrl.startsWith(apiMatchUrl)) { return apiMatchUrl; } } return apiUrl; }}
2、改造Api入口,实现请求参数入参
// 判断是否为通配符规则if (ConfigDictionaryConstants.ApiType.DEFAULT != apiInfo.getApiType()) { List<ApiMatchBO> parseParamConfig = apiInfo.getParseParamConfig(); if (ArrayUtils.isNotEmpty(parseParamConfig)) { String[] apiUrls = apiUrl.split("/"); for (ApiMatchBO apiMatch : parseParamConfig) { if (apiUrls.length > apiMatch.getIndex()) { requestParams.put(apiMatch.getKey(), apiUrls[apiMatch.getIndex()]); } } }}
- 代码执行结果
发表回复
评论列表(0条)