Skip to content

Commit 7f024d4

Browse files
committed
新增 javax 来兼容 JDK 1.8~16 及 SpringBoot 1.4~2.7 等
1 parent a4e8c47 commit 7f024d4

File tree

5 files changed

+602
-2
lines changed

5 files changed

+602
-2
lines changed

pom.xml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
<groupId>apijson.router</groupId>
77
<artifactId>apijson-router</artifactId>
8-
<version>2.1.0</version>
8+
<version>2.1.5</version>
99
<packaging>jar</packaging>
1010

1111
<name>APIJSONRouter</name>
@@ -21,13 +21,21 @@
2121
</properties>
2222

2323
<dependencies>
24+
<!-- JDK 17+, SpringBoot 3.0+ -->
2425
<dependency>
2526
<groupId>jakarta.servlet</groupId>
2627
<artifactId>jakarta.servlet-api</artifactId>
2728
<version>6.0.0</version>
2829
<scope>provided</scope>
2930
</dependency>
3031

32+
<!-- JDK 1.8~16, SpringBoot 1.4~2.7 -->
33+
<dependency>
34+
<groupId>javax.servlet</groupId>
35+
<artifactId>javax.servlet-api</artifactId>
36+
<version>4.0.1</version>
37+
</dependency>
38+
3139
<dependency>
3240
<groupId>com.alibaba</groupId>
3341
<artifactId>fastjson</artifactId>
@@ -43,7 +51,7 @@
4351
<dependency>
4452
<groupId>com.github.APIJSON</groupId>
4553
<artifactId>apijson-framework</artifactId>
46-
<version>7.1.0</version>
54+
<version>7.1.5</version>
4755
</dependency>
4856
</dependencies>
4957

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*Copyright ©2022 APIJSON(https://github.com/APIJSON)
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.*/
14+
15+
package apijson.router.javax;
16+
17+
import apijson.NotNull;
18+
import apijson.framework.javax.APIJSONApplication;
19+
import apijson.framework.javax.APIJSONCreator;
20+
21+
22+
/**启动入口 Application 基类
23+
* 右键这个类 > Run As > Java Application
24+
* @author Lemon
25+
*/
26+
public class APIJSONRouterApplication extends APIJSONApplication {
27+
public static final String TAG = "APIJSONRouterApplication";
28+
29+
/**初始化,加载所有配置并校验
30+
* @return
31+
* @throws Exception
32+
*/
33+
public static void init() throws Exception {
34+
init(true, DEFAULT_APIJSON_CREATOR);
35+
}
36+
/**初始化,加载所有配置并校验
37+
* @param shutdownWhenServerError
38+
* @return
39+
* @throws Exception
40+
*/
41+
public static void init(boolean shutdownWhenServerError) throws Exception {
42+
init(shutdownWhenServerError, DEFAULT_APIJSON_CREATOR);
43+
}
44+
/**初始化,加载所有配置并校验
45+
* @param creator
46+
* @return
47+
* @throws Exception
48+
*/
49+
public static <T extends Object> void init(@NotNull APIJSONCreator<T> creator) throws Exception {
50+
init(true, creator);
51+
}
52+
/**初始化,加载所有配置并校验
53+
* @param shutdownWhenServerError
54+
* @param creator
55+
* @return
56+
* @throws Exception
57+
*/
58+
public static <T extends Object> void init(boolean shutdownWhenServerError, @NotNull APIJSONCreator<T> creator) throws Exception {
59+
// 避免多个插件重复调用这句 APIJSONApplication.init(shutdownWhenServerError, creator);
60+
System.out.println("\n\n\n\n\n<<<<<<<<<<<<<<<<<<<<<<<<< APIJSON Router 开始启动 >>>>>>>>>>>>>>>>>>>>>>>>\n");
61+
62+
63+
System.out.println("\n\n\n开始初始化: Document 请求映射配置 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
64+
try {
65+
APIJSONRouterVerifier.initDocument(shutdownWhenServerError, creator);
66+
}
67+
catch (Throwable e) {
68+
e.printStackTrace();
69+
if (shutdownWhenServerError) {
70+
onServerError("Document 请求映射配置 初始化失败!", shutdownWhenServerError);
71+
}
72+
}
73+
System.out.println("\n完成初始化: Document 请求映射配置 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
74+
75+
System.out.println("\n插件地址: https://github.com/APIJSON/apijson-router");
76+
System.out.println("\n\n<<<<<<<<<<<<<<<<<<<<<<<<< APIJSON Router 启动完成,试试调用类 RESTful API 吧 ^_^ >>>>>>>>>>>>>>>>>>>>>>>>\n");
77+
}
78+
79+
}
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
/*Copyright ©2022 APIJSON(https://github.com/APIJSON)
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.*/
14+
15+
package apijson.router.javax;
16+
17+
import apijson.*;
18+
import apijson.framework.javax.APIJSONConstant;
19+
import apijson.framework.javax.APIJSONController;
20+
import apijson.framework.javax.APIJSONCreator;
21+
import apijson.framework.javax.APIJSONParser;
22+
import apijson.orm.AbstractVerifier;
23+
import apijson.orm.Parser;
24+
import apijson.orm.SQLConfig;
25+
import apijson.orm.Verifier;
26+
import com.alibaba.fastjson.JSONObject;
27+
import javax.servlet.http.HttpSession;
28+
29+
import java.util.*;
30+
import java.util.Map.Entry;
31+
32+
import static apijson.RequestMethod.GET;
33+
import static apijson.framework.javax.APIJSONConstant.METHODS;
34+
35+
36+
/**APIJSON router controller,建议在子项目被 @RestController 注解的类继承它或通过它的实例调用相关方法
37+
* @author Lemon
38+
*/
39+
public class APIJSONRouterController<T extends Object> extends APIJSONController<T> {
40+
public static final String TAG = "APIJSONRouterController";
41+
42+
//通用接口,非事务型操作 和 简单事务型操作 都可通过这些接口自动化实现<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
43+
44+
/**增删改查统一的类 RESTful API 入口,牺牲一些路由解析性能来提升一点开发效率
45+
* compatCommonAPI = Log.DEBUG
46+
* @param method
47+
* @param tag
48+
* @param params
49+
* @param request
50+
* @param session
51+
* @return
52+
*/
53+
public String router(String method, String tag, Map<String, String> params, String request, HttpSession session) {
54+
return router(method, tag, params, request, session, Log.DEBUG);
55+
}
56+
/**增删改查统一的类 RESTful API 入口,牺牲一些路由解析性能来提升一点开发效率
57+
* @param method
58+
* @param tag
59+
* @param params
60+
* @param request
61+
* @param session
62+
* @param compatCommonAPI 兼容万能通用 API,当没有映射 APIJSON 格式请求时,自动转到万能通用 API
63+
* @return
64+
*/
65+
public String router(String method, String tag, Map<String, String> params, String request, HttpSession session, boolean compatCommonAPI) {
66+
if (METHODS.contains(method) == false) {
67+
return APIJSONParser.newErrorResult(new IllegalArgumentException("URL 路径 /{method}/{tag} 中 method 值 " + method
68+
+ " 错误!只允许 " + METHODS + " 中的一个!")).toJSONString();
69+
}
70+
71+
String t = compatCommonAPI && tag != null && tag.endsWith("[]") ? tag.substring(0, tag.length() - 2) : tag;
72+
if (StringUtil.isName(t) == false) {
73+
return APIJSONParser.newErrorResult(new IllegalArgumentException("URL 路径 /" + method + "/{tag} 的 tag 中 " + t
74+
+ " 错误!tag 不能为空,且只允许变量命名格式!")).toJSONString();
75+
}
76+
77+
String versionStr = params == null ? null : params.remove(APIJSONConstant.VERSION);
78+
Integer version;
79+
try {
80+
version = StringUtil.isEmpty(versionStr, false) ? null : Integer.valueOf(versionStr);
81+
}
82+
catch (Exception e) {
83+
return APIJSONParser.newErrorResult(new IllegalArgumentException("URL 路径 /" + method
84+
+ "/" + tag + "?version=value 中 value 值 " + versionStr + " 错误!必须符合整数格式!")).toJSONString();
85+
}
86+
87+
if (version == null) {
88+
version = 0;
89+
}
90+
91+
try {
92+
// 从 Document 查这样的接口
93+
String cacheKey = AbstractVerifier.getCacheKeyForRequest(method, tag);
94+
SortedMap<Integer, JSONObject> versionedMap = APIJSONRouterVerifier.DOCUMENT_MAP.get(cacheKey);
95+
96+
JSONObject result = versionedMap == null ? null : versionedMap.get(version);
97+
if (result == null) { // version <= 0 时使用最新,version > 0 时使用 > version 的最接近版本(最小版本)
98+
Set<Entry<Integer, JSONObject>> set = versionedMap == null ? null : versionedMap.entrySet();
99+
100+
if (set != null && set.isEmpty() == false) {
101+
Entry<Integer, JSONObject> maxEntry = null;
102+
103+
for (Entry<Integer, JSONObject> entry : set) {
104+
if (entry == null || entry.getKey() == null || entry.getValue() == null) {
105+
continue;
106+
}
107+
108+
if (version == null || version <= 0 || version == entry.getKey()) { // 这里应该不会出现相等,因为上面 versionedMap.get(Integer.valueOf(version))
109+
maxEntry = entry;
110+
break;
111+
}
112+
113+
if (entry.getKey() < version) {
114+
break;
115+
}
116+
117+
maxEntry = entry;
118+
}
119+
120+
result = maxEntry == null ? null : maxEntry.getValue();
121+
}
122+
123+
if (result != null) { // 加快下次查询,查到值的话组合情况其实是有限的,不属于恶意请求
124+
if (versionedMap == null) {
125+
versionedMap = new TreeMap<>((o1, o2) -> {
126+
return o2 == null ? -1 : o2.compareTo(o1); // 降序
127+
});
128+
}
129+
130+
versionedMap.put(version, result);
131+
APIJSONRouterVerifier.DOCUMENT_MAP.put(cacheKey, versionedMap);
132+
}
133+
}
134+
135+
@SuppressWarnings("unchecked")
136+
APIJSONCreator<T> creator = (APIJSONCreator<T>) APIJSONParser.APIJSON_CREATOR;
137+
if (result == null && Log.DEBUG && APIJSONRouterVerifier.DOCUMENT_MAP.isEmpty()) {
138+
139+
//获取指定的JSON结构 <<<<<<<<<<<<<<
140+
SQLConfig config = creator.createSQLConfig().setMethod(GET).setTable(APIJSONConstant.DOCUMENT_);
141+
config.setPrepared(false);
142+
config.setColumn(Arrays.asList("request,apijson"));
143+
144+
Map<String, Object> where = new HashMap<String, Object>();
145+
where.put("url", "/" + method + "/" + tag);
146+
where.put("apijson{}", "length(apijson)>0");
147+
148+
if (version > 0) {
149+
where.put(JSONRequest.KEY_VERSION + ">=", version);
150+
}
151+
config.setWhere(where);
152+
config.setOrder(JSONRequest.KEY_VERSION + (version > 0 ? "+" : "-"));
153+
config.setCount(1);
154+
155+
//too many connections error: 不try-catch,可以让客户端看到是服务器内部异常
156+
result = creator.createSQLExecutor().execute(config, false);
157+
158+
// version, method, tag 组合情况太多了,JDK 里又没有 LRUCache,所以要么启动时一次性缓存全部后面只用缓存,要么每次都查数据库
159+
// versionedMap.put(Integer.valueOf(version), result);
160+
// DOCUMENT_MAP.put(cacheKey, versionedMap);
161+
}
162+
163+
String apijson = result == null ? null : result.getString("apijson");
164+
if (StringUtil.isEmpty(apijson, true)) { //
165+
if (compatCommonAPI) {
166+
return crudByTag(method, tag, params, request, session);
167+
}
168+
169+
throw new IllegalArgumentException("URL 路径 /" + method
170+
+ "/" + tag + (versionStr == null ? "" : "?version=" + versionStr) + " 对应的接口不存在!");
171+
}
172+
173+
JSONObject rawReq = JSON.parseObject(request);
174+
if (rawReq == null) {
175+
rawReq = new JSONObject(true);
176+
}
177+
if (params != null && params.isEmpty() == false) {
178+
rawReq.putAll(params);
179+
}
180+
181+
RequestMethod requestMethod = RequestMethod.valueOf(method.toUpperCase());
182+
Parser<T> parser = newParser(session, requestMethod);
183+
184+
if (parser.isNeedVerifyContent()) {
185+
Verifier<T> verifier = creator.createVerifier();
186+
187+
//获取指定的JSON结构 <<<<<<<<<<<<
188+
JSONObject object;
189+
object = parser.getStructure("Request", method.toUpperCase(), tag, version);
190+
if (object == null) { //empty表示随意操作 || object.isEmpty()) {
191+
throw new UnsupportedOperationException("找不到 version: " + version + ", method: " + method.toUpperCase() + ", tag: " + tag + " 对应的 structure !"
192+
+ "非开放请求必须是后端 Request 表中校验规则允许的操作!如果需要则在 Request 表中新增配置!");
193+
}
194+
195+
JSONObject target = object;
196+
197+
//JSONObject clone 浅拷贝没用,Structure.parse 会导致 structure 里面被清空,第二次从缓存里取到的就是 {}
198+
verifier.verifyRequest(requestMethod, "", target, rawReq, 0, null, null, creator);
199+
}
200+
201+
JSONObject apijsonReq = JSON.parseObject(apijson);
202+
if (apijsonReq == null) {
203+
apijsonReq = new JSONObject(true);
204+
}
205+
206+
Set<Entry<String, Object>> rawSet = rawReq.entrySet();
207+
if (rawSet != null && rawSet.isEmpty() == false) {
208+
for (Entry<String, Object> entry : rawSet) {
209+
String key = entry == null ? null : entry.getKey();
210+
if (key == null) { // value 为 null 有效
211+
continue;
212+
}
213+
214+
String[] pathKeys = key.split("\\.");
215+
//逐层到达child的直接容器JSONObject parent
216+
int last = pathKeys.length - 1;
217+
JSONObject parent = apijsonReq;
218+
for (int i = 0; i < last; i++) {//一步一步到达指定位置
219+
JSONObject p = parent.getJSONObject(pathKeys[i]);
220+
if (p == null) {
221+
p = new JSONObject(true);
222+
parent.put(key, p);
223+
}
224+
parent = p;
225+
}
226+
227+
parent.put(pathKeys[last], entry.getValue());
228+
}
229+
}
230+
231+
// 没必要,已经是预设好的实际参数了,如果要 tag 就在 apijson 字段配置 apijsonReq.put(JSONRequest.KEY_TAG, tag);
232+
233+
return parser.setNeedVerifyContent(false).parse(apijsonReq);
234+
}
235+
catch (Exception e) {
236+
return APIJSONParser.newErrorResult(e).toJSONString();
237+
}
238+
}
239+
240+
241+
}

0 commit comments

Comments
 (0)