-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
新增 javax 来兼容 JDK 1.8~16 及 SpringBoot 1.4~2.7 等
- Loading branch information
1 parent
a4e8c47
commit 7f024d4
Showing
5 changed files
with
602 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
79 changes: 79 additions & 0 deletions
79
src/main/java/apijson/router/javax/APIJSONRouterApplication.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/*Copyright ©2022 APIJSON(https://github.com/APIJSON) | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License.*/ | ||
|
||
package apijson.router.javax; | ||
|
||
import apijson.NotNull; | ||
import apijson.framework.javax.APIJSONApplication; | ||
import apijson.framework.javax.APIJSONCreator; | ||
|
||
|
||
/**启动入口 Application 基类 | ||
* 右键这个类 > Run As > Java Application | ||
* @author Lemon | ||
*/ | ||
public class APIJSONRouterApplication extends APIJSONApplication { | ||
public static final String TAG = "APIJSONRouterApplication"; | ||
|
||
/**初始化,加载所有配置并校验 | ||
* @return | ||
* @throws Exception | ||
*/ | ||
public static void init() throws Exception { | ||
init(true, DEFAULT_APIJSON_CREATOR); | ||
} | ||
/**初始化,加载所有配置并校验 | ||
* @param shutdownWhenServerError | ||
* @return | ||
* @throws Exception | ||
*/ | ||
public static void init(boolean shutdownWhenServerError) throws Exception { | ||
init(shutdownWhenServerError, DEFAULT_APIJSON_CREATOR); | ||
} | ||
/**初始化,加载所有配置并校验 | ||
* @param creator | ||
* @return | ||
* @throws Exception | ||
*/ | ||
public static <T extends Object> void init(@NotNull APIJSONCreator<T> creator) throws Exception { | ||
init(true, creator); | ||
} | ||
/**初始化,加载所有配置并校验 | ||
* @param shutdownWhenServerError | ||
* @param creator | ||
* @return | ||
* @throws Exception | ||
*/ | ||
public static <T extends Object> void init(boolean shutdownWhenServerError, @NotNull APIJSONCreator<T> creator) throws Exception { | ||
// 避免多个插件重复调用这句 APIJSONApplication.init(shutdownWhenServerError, creator); | ||
System.out.println("\n\n\n\n\n<<<<<<<<<<<<<<<<<<<<<<<<< APIJSON Router 开始启动 >>>>>>>>>>>>>>>>>>>>>>>>\n"); | ||
|
||
|
||
System.out.println("\n\n\n开始初始化: Document 请求映射配置 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); | ||
try { | ||
APIJSONRouterVerifier.initDocument(shutdownWhenServerError, creator); | ||
} | ||
catch (Throwable e) { | ||
e.printStackTrace(); | ||
if (shutdownWhenServerError) { | ||
onServerError("Document 请求映射配置 初始化失败!", shutdownWhenServerError); | ||
} | ||
} | ||
System.out.println("\n完成初始化: Document 请求映射配置 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); | ||
|
||
System.out.println("\n插件地址: https://github.com/APIJSON/apijson-router"); | ||
System.out.println("\n\n<<<<<<<<<<<<<<<<<<<<<<<<< APIJSON Router 启动完成,试试调用类 RESTful API 吧 ^_^ >>>>>>>>>>>>>>>>>>>>>>>>\n"); | ||
} | ||
|
||
} |
241 changes: 241 additions & 0 deletions
241
src/main/java/apijson/router/javax/APIJSONRouterController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
/*Copyright ©2022 APIJSON(https://github.com/APIJSON) | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License.*/ | ||
|
||
package apijson.router.javax; | ||
|
||
import apijson.*; | ||
import apijson.framework.javax.APIJSONConstant; | ||
import apijson.framework.javax.APIJSONController; | ||
import apijson.framework.javax.APIJSONCreator; | ||
import apijson.framework.javax.APIJSONParser; | ||
import apijson.orm.AbstractVerifier; | ||
import apijson.orm.Parser; | ||
import apijson.orm.SQLConfig; | ||
import apijson.orm.Verifier; | ||
import com.alibaba.fastjson.JSONObject; | ||
import javax.servlet.http.HttpSession; | ||
|
||
import java.util.*; | ||
import java.util.Map.Entry; | ||
|
||
import static apijson.RequestMethod.GET; | ||
import static apijson.framework.javax.APIJSONConstant.METHODS; | ||
|
||
|
||
/**APIJSON router controller,建议在子项目被 @RestController 注解的类继承它或通过它的实例调用相关方法 | ||
* @author Lemon | ||
*/ | ||
public class APIJSONRouterController<T extends Object> extends APIJSONController<T> { | ||
public static final String TAG = "APIJSONRouterController"; | ||
|
||
//通用接口,非事务型操作 和 简单事务型操作 都可通过这些接口自动化实现<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< | ||
|
||
/**增删改查统一的类 RESTful API 入口,牺牲一些路由解析性能来提升一点开发效率 | ||
* compatCommonAPI = Log.DEBUG | ||
* @param method | ||
* @param tag | ||
* @param params | ||
* @param request | ||
* @param session | ||
* @return | ||
*/ | ||
public String router(String method, String tag, Map<String, String> params, String request, HttpSession session) { | ||
return router(method, tag, params, request, session, Log.DEBUG); | ||
} | ||
/**增删改查统一的类 RESTful API 入口,牺牲一些路由解析性能来提升一点开发效率 | ||
* @param method | ||
* @param tag | ||
* @param params | ||
* @param request | ||
* @param session | ||
* @param compatCommonAPI 兼容万能通用 API,当没有映射 APIJSON 格式请求时,自动转到万能通用 API | ||
* @return | ||
*/ | ||
public String router(String method, String tag, Map<String, String> params, String request, HttpSession session, boolean compatCommonAPI) { | ||
if (METHODS.contains(method) == false) { | ||
return APIJSONParser.newErrorResult(new IllegalArgumentException("URL 路径 /{method}/{tag} 中 method 值 " + method | ||
+ " 错误!只允许 " + METHODS + " 中的一个!")).toJSONString(); | ||
} | ||
|
||
String t = compatCommonAPI && tag != null && tag.endsWith("[]") ? tag.substring(0, tag.length() - 2) : tag; | ||
if (StringUtil.isName(t) == false) { | ||
return APIJSONParser.newErrorResult(new IllegalArgumentException("URL 路径 /" + method + "/{tag} 的 tag 中 " + t | ||
+ " 错误!tag 不能为空,且只允许变量命名格式!")).toJSONString(); | ||
} | ||
|
||
String versionStr = params == null ? null : params.remove(APIJSONConstant.VERSION); | ||
Integer version; | ||
try { | ||
version = StringUtil.isEmpty(versionStr, false) ? null : Integer.valueOf(versionStr); | ||
} | ||
catch (Exception e) { | ||
return APIJSONParser.newErrorResult(new IllegalArgumentException("URL 路径 /" + method | ||
+ "/" + tag + "?version=value 中 value 值 " + versionStr + " 错误!必须符合整数格式!")).toJSONString(); | ||
} | ||
|
||
if (version == null) { | ||
version = 0; | ||
} | ||
|
||
try { | ||
// 从 Document 查这样的接口 | ||
String cacheKey = AbstractVerifier.getCacheKeyForRequest(method, tag); | ||
SortedMap<Integer, JSONObject> versionedMap = APIJSONRouterVerifier.DOCUMENT_MAP.get(cacheKey); | ||
|
||
JSONObject result = versionedMap == null ? null : versionedMap.get(version); | ||
if (result == null) { // version <= 0 时使用最新,version > 0 时使用 > version 的最接近版本(最小版本) | ||
Set<Entry<Integer, JSONObject>> set = versionedMap == null ? null : versionedMap.entrySet(); | ||
|
||
if (set != null && set.isEmpty() == false) { | ||
Entry<Integer, JSONObject> maxEntry = null; | ||
|
||
for (Entry<Integer, JSONObject> entry : set) { | ||
if (entry == null || entry.getKey() == null || entry.getValue() == null) { | ||
continue; | ||
} | ||
|
||
if (version == null || version <= 0 || version == entry.getKey()) { // 这里应该不会出现相等,因为上面 versionedMap.get(Integer.valueOf(version)) | ||
maxEntry = entry; | ||
break; | ||
} | ||
|
||
if (entry.getKey() < version) { | ||
break; | ||
} | ||
|
||
maxEntry = entry; | ||
} | ||
|
||
result = maxEntry == null ? null : maxEntry.getValue(); | ||
} | ||
|
||
if (result != null) { // 加快下次查询,查到值的话组合情况其实是有限的,不属于恶意请求 | ||
if (versionedMap == null) { | ||
versionedMap = new TreeMap<>((o1, o2) -> { | ||
return o2 == null ? -1 : o2.compareTo(o1); // 降序 | ||
}); | ||
} | ||
|
||
versionedMap.put(version, result); | ||
APIJSONRouterVerifier.DOCUMENT_MAP.put(cacheKey, versionedMap); | ||
} | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
APIJSONCreator<T> creator = (APIJSONCreator<T>) APIJSONParser.APIJSON_CREATOR; | ||
if (result == null && Log.DEBUG && APIJSONRouterVerifier.DOCUMENT_MAP.isEmpty()) { | ||
|
||
//获取指定的JSON结构 <<<<<<<<<<<<<< | ||
SQLConfig config = creator.createSQLConfig().setMethod(GET).setTable(APIJSONConstant.DOCUMENT_); | ||
config.setPrepared(false); | ||
config.setColumn(Arrays.asList("request,apijson")); | ||
|
||
Map<String, Object> where = new HashMap<String, Object>(); | ||
where.put("url", "/" + method + "/" + tag); | ||
where.put("apijson{}", "length(apijson)>0"); | ||
|
||
if (version > 0) { | ||
where.put(JSONRequest.KEY_VERSION + ">=", version); | ||
} | ||
config.setWhere(where); | ||
config.setOrder(JSONRequest.KEY_VERSION + (version > 0 ? "+" : "-")); | ||
config.setCount(1); | ||
|
||
//too many connections error: 不try-catch,可以让客户端看到是服务器内部异常 | ||
result = creator.createSQLExecutor().execute(config, false); | ||
|
||
// version, method, tag 组合情况太多了,JDK 里又没有 LRUCache,所以要么启动时一次性缓存全部后面只用缓存,要么每次都查数据库 | ||
// versionedMap.put(Integer.valueOf(version), result); | ||
// DOCUMENT_MAP.put(cacheKey, versionedMap); | ||
} | ||
|
||
String apijson = result == null ? null : result.getString("apijson"); | ||
if (StringUtil.isEmpty(apijson, true)) { // | ||
if (compatCommonAPI) { | ||
return crudByTag(method, tag, params, request, session); | ||
} | ||
|
||
throw new IllegalArgumentException("URL 路径 /" + method | ||
+ "/" + tag + (versionStr == null ? "" : "?version=" + versionStr) + " 对应的接口不存在!"); | ||
} | ||
|
||
JSONObject rawReq = JSON.parseObject(request); | ||
if (rawReq == null) { | ||
rawReq = new JSONObject(true); | ||
} | ||
if (params != null && params.isEmpty() == false) { | ||
rawReq.putAll(params); | ||
} | ||
|
||
RequestMethod requestMethod = RequestMethod.valueOf(method.toUpperCase()); | ||
Parser<T> parser = newParser(session, requestMethod); | ||
|
||
if (parser.isNeedVerifyContent()) { | ||
Verifier<T> verifier = creator.createVerifier(); | ||
|
||
//获取指定的JSON结构 <<<<<<<<<<<< | ||
JSONObject object; | ||
object = parser.getStructure("Request", method.toUpperCase(), tag, version); | ||
if (object == null) { //empty表示随意操作 || object.isEmpty()) { | ||
throw new UnsupportedOperationException("找不到 version: " + version + ", method: " + method.toUpperCase() + ", tag: " + tag + " 对应的 structure !" | ||
+ "非开放请求必须是后端 Request 表中校验规则允许的操作!如果需要则在 Request 表中新增配置!"); | ||
} | ||
|
||
JSONObject target = object; | ||
|
||
//JSONObject clone 浅拷贝没用,Structure.parse 会导致 structure 里面被清空,第二次从缓存里取到的就是 {} | ||
verifier.verifyRequest(requestMethod, "", target, rawReq, 0, null, null, creator); | ||
} | ||
|
||
JSONObject apijsonReq = JSON.parseObject(apijson); | ||
if (apijsonReq == null) { | ||
apijsonReq = new JSONObject(true); | ||
} | ||
|
||
Set<Entry<String, Object>> rawSet = rawReq.entrySet(); | ||
if (rawSet != null && rawSet.isEmpty() == false) { | ||
for (Entry<String, Object> entry : rawSet) { | ||
String key = entry == null ? null : entry.getKey(); | ||
if (key == null) { // value 为 null 有效 | ||
continue; | ||
} | ||
|
||
String[] pathKeys = key.split("\\."); | ||
//逐层到达child的直接容器JSONObject parent | ||
int last = pathKeys.length - 1; | ||
JSONObject parent = apijsonReq; | ||
for (int i = 0; i < last; i++) {//一步一步到达指定位置 | ||
JSONObject p = parent.getJSONObject(pathKeys[i]); | ||
if (p == null) { | ||
p = new JSONObject(true); | ||
parent.put(key, p); | ||
} | ||
parent = p; | ||
} | ||
|
||
parent.put(pathKeys[last], entry.getValue()); | ||
} | ||
} | ||
|
||
// 没必要,已经是预设好的实际参数了,如果要 tag 就在 apijson 字段配置 apijsonReq.put(JSONRequest.KEY_TAG, tag); | ||
|
||
return parser.setNeedVerifyContent(false).parse(apijsonReq); | ||
} | ||
catch (Exception e) { | ||
return APIJSONParser.newErrorResult(e).toJSONString(); | ||
} | ||
} | ||
|
||
|
||
} |
Oops, something went wrong.