|
15 | 15 | package apijson.framework; |
16 | 16 |
|
17 | 17 | import apijson.*; |
18 | | -import apijson.orm.AbstractParser; |
19 | | -import apijson.orm.Parser; |
20 | | -import apijson.orm.Visitor; |
| 18 | +import apijson.JSONRequest; |
| 19 | +import apijson.orm.*; |
21 | 20 |
|
22 | 21 | import jakarta.servlet.http.HttpSession; |
23 | 22 |
|
24 | 23 | import java.rmi.ServerException; |
25 | | -import java.util.List; |
26 | | -import java.util.Map; |
| 24 | +import java.util.*; |
27 | 25 |
|
28 | | -import static apijson.JSON.toJSONString; |
| 26 | +import static apijson.JSON.*; |
29 | 27 | import static apijson.RequestMethod.*; |
30 | 28 | import static apijson.framework.APIJSONConstant.*; |
31 | 29 |
|
@@ -283,6 +281,218 @@ public String putByTag(String tag, Map<String, String> params, String request, H |
283 | 281 | public String deleteByTag(String tag, Map<String, String> params, String request, HttpSession session) { |
284 | 282 | return parseByTag(DELETE, tag, params, request, session); |
285 | 283 | } |
| 284 | + //通用接口,非事务型操作 和 简单事务型操作 都可通过这些接口自动化实现<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< |
| 285 | + |
| 286 | + /**增删改查统一的类 RESTful API 入口,牺牲一些路由解析性能来提升一点开发效率 |
| 287 | + * compatCommonAPI = Log.DEBUG |
| 288 | + * @param method |
| 289 | + * @param tag |
| 290 | + * @param params |
| 291 | + * @param request |
| 292 | + * @param session |
| 293 | + * @return |
| 294 | + */ |
| 295 | + public String router(String method, String tag, Map<String, String> params, String request, HttpSession session) { |
| 296 | + return router(method, tag, params, request, session, Log.DEBUG); |
| 297 | + } |
| 298 | + /**增删改查统一的类 RESTful API 入口,牺牲一些路由解析性能来提升一点开发效率 |
| 299 | + * @param method |
| 300 | + * @param tag |
| 301 | + * @param params |
| 302 | + * @param request |
| 303 | + * @param session |
| 304 | + * @param compatCommonAPI 兼容万能通用 API,当没有映射 APIJSON 格式请求时,自动转到万能通用 API |
| 305 | + * @return |
| 306 | + */ |
| 307 | + public String router(String method, String tag, Map<String, String> params, String request, HttpSession session, boolean compatCommonAPI) { |
| 308 | + RequestMethod requestMethod = null; |
| 309 | + try { |
| 310 | + requestMethod = RequestMethod.valueOf(method.toUpperCase()); |
| 311 | + } catch (Throwable e) { |
| 312 | + // 下方 METHODS.contains(method) 会抛异常 |
| 313 | + } |
| 314 | + Parser<T, M, L> parser = newParser(session, requestMethod); |
| 315 | + |
| 316 | + if (METHODS.contains(method) == false) { |
| 317 | + return JSON.toJSONString( |
| 318 | + parser.newErrorResult( |
| 319 | + new IllegalArgumentException("URL 路径 /{method}/{tag} 中 method 值 " |
| 320 | + + method + " 错误!只允许 " + METHODS + " 中的一个!" |
| 321 | + ) |
| 322 | + ) |
| 323 | + ); |
| 324 | + } |
| 325 | + |
| 326 | + String t = compatCommonAPI && tag != null && tag.endsWith("[]") ? tag.substring(0, tag.length() - 2) : tag; |
| 327 | + if (StringUtil.isName(t) == false) { |
| 328 | + return JSON.toJSONString( |
| 329 | + parser.newErrorResult( |
| 330 | + new IllegalArgumentException("URL 路径 /" + method + "/{tag} 的 tag 中 " |
| 331 | + + t + " 错误!tag 不能为空,且只允许变量命名格式!" |
| 332 | + ) |
| 333 | + ) |
| 334 | + ); |
| 335 | + } |
| 336 | + |
| 337 | + String versionStr = params == null ? null : params.remove(APIJSONConstant.VERSION); |
| 338 | + Integer version; |
| 339 | + try { |
| 340 | + version = StringUtil.isEmpty(versionStr, false) ? null : Integer.valueOf(versionStr); |
| 341 | + } |
| 342 | + catch (Exception e) { |
| 343 | + return JSON.toJSONString( |
| 344 | + parser.newErrorResult(new IllegalArgumentException("URL 路径 /" + method + "/" |
| 345 | + + tag + "?version=value 中 value 值 " + versionStr + " 错误!必须符合整数格式!") |
| 346 | + ) |
| 347 | + ); |
| 348 | + } |
| 349 | + |
| 350 | + if (version == null) { |
| 351 | + version = 0; |
| 352 | + } |
| 353 | + |
| 354 | + try { |
| 355 | + // 从 Document 查这样的接口 |
| 356 | + String cacheKey = AbstractVerifier.getCacheKeyForRequest(method, tag); |
| 357 | + SortedMap<Integer, Map<String, Object>> versionedMap = APIJSONVerifier.DOCUMENT_MAP.get(cacheKey); |
| 358 | + |
| 359 | + Map<String, Object> result = versionedMap == null ? null : versionedMap.get(version); |
| 360 | + if (result == null) { // version <= 0 时使用最新,version > 0 时使用 > version 的最接近版本(最小版本) |
| 361 | + Set<Map.Entry<Integer, Map<String, Object>>> set = versionedMap == null ? null : versionedMap.entrySet(); |
| 362 | + |
| 363 | + if (set != null && set.isEmpty() == false) { |
| 364 | + Map.Entry<Integer, Map<String, Object>> maxEntry = null; |
| 365 | + |
| 366 | + for (Map.Entry<Integer, Map<String, Object>> entry : set) { |
| 367 | + if (entry == null || entry.getKey() == null || entry.getValue() == null) { |
| 368 | + continue; |
| 369 | + } |
| 370 | + |
| 371 | + if (version == null || version <= 0 || version == entry.getKey()) { // 这里应该不会出现相等,因为上面 versionedMap.get(Integer.valueOf(version)) |
| 372 | + maxEntry = entry; |
| 373 | + break; |
| 374 | + } |
| 375 | + |
| 376 | + if (entry.getKey() < version) { |
| 377 | + break; |
| 378 | + } |
| 379 | + |
| 380 | + maxEntry = entry; |
| 381 | + } |
| 382 | + |
| 383 | + result = maxEntry == null ? null : maxEntry.getValue(); |
| 384 | + } |
| 385 | + |
| 386 | + if (result != null) { // 加快下次查询,查到值的话组合情况其实是有限的,不属于恶意请求 |
| 387 | + if (versionedMap == null) { |
| 388 | + versionedMap = new TreeMap<>((o1, o2) -> { |
| 389 | + return o2 == null ? -1 : o2.compareTo(o1); // 降序 |
| 390 | + }); |
| 391 | + } |
| 392 | + |
| 393 | + versionedMap.put(version, result); |
| 394 | + APIJSONVerifier.DOCUMENT_MAP.put(cacheKey, versionedMap); |
| 395 | + } |
| 396 | + } |
| 397 | + |
| 398 | + @SuppressWarnings("unchecked") |
| 399 | + APIJSONCreator<T, M, L> creator = (APIJSONCreator<T, M, L>) APIJSONParser.APIJSON_CREATOR; |
| 400 | + if (result == null && Log.DEBUG && APIJSONVerifier.DOCUMENT_MAP.isEmpty()) { |
| 401 | + |
| 402 | + //获取指定的JSON结构 <<<<<<<<<<<<<< |
| 403 | + SQLConfig<T, M, L> config = creator.createSQLConfig().setMethod(GET).setTable(APIJSONConstant.DOCUMENT_); |
| 404 | + config.setPrepared(false); |
| 405 | + config.setColumn(Arrays.asList("request,apijson")); |
| 406 | + |
| 407 | + Map<String, Object> where = new HashMap<String, Object>(); |
| 408 | + where.put("url", "/" + method + "/" + tag); |
| 409 | + where.put("apijson{}", "length(apijson)>0"); |
| 410 | + |
| 411 | + if (version > 0) { |
| 412 | + where.put(JSONRequest.KEY_VERSION + ">=", version); |
| 413 | + } |
| 414 | + config.setWhere(where); |
| 415 | + config.setOrder(JSONRequest.KEY_VERSION + (version > 0 ? "+" : "-")); |
| 416 | + config.setCount(1); |
| 417 | + |
| 418 | + //too many connections error: 不try-catch,可以让客户端看到是服务器内部异常 |
| 419 | + result = creator.createSQLExecutor().execute(config, false); |
| 420 | + |
| 421 | + // version, method, tag 组合情况太多了,JDK 里又没有 LRUCache,所以要么启动时一次性缓存全部后面只用缓存,要么每次都查数据库 |
| 422 | + // versionedMap.put(Integer.valueOf(version), result); |
| 423 | + // DOCUMENT_MAP.put(cacheKey, versionedMap); |
| 424 | + } |
| 425 | + |
| 426 | + String apijson = result == null ? null : getString(result, "apijson"); |
| 427 | + if (StringUtil.isEmpty(apijson, true)) { // |
| 428 | + if (compatCommonAPI) { |
| 429 | + return crudByTag(method, tag, params, request, session); |
| 430 | + } |
| 431 | + |
| 432 | + throw new IllegalArgumentException("URL 路径 /" + method |
| 433 | + + "/" + tag + (versionStr == null ? "" : "?version=" + versionStr) + " 对应的接口不存在!"); |
| 434 | + } |
| 435 | + |
| 436 | + M rawReq = JSON.parseObject(request); |
| 437 | + if (rawReq == null) { |
| 438 | + rawReq = JSON.createJSONObject(); |
| 439 | + } |
| 440 | + if (params != null && params.isEmpty() == false) { |
| 441 | + rawReq.putAll(params); |
| 442 | + } |
| 443 | + |
| 444 | + if (parser.isNeedVerifyContent()) { |
| 445 | + Verifier<T, M, L> verifier = creator.createVerifier(); |
| 446 | + |
| 447 | + //获取指定的JSON结构 <<<<<<<<<<<< |
| 448 | + Map<String, Object> target = parser.getStructure("Request", method.toUpperCase(), tag, version); |
| 449 | + if (target == null) { //empty表示随意操作 || object.isEmpty()) { |
| 450 | + throw new UnsupportedOperationException("找不到 version: " + version + ", method: " + method.toUpperCase() + ", tag: " + tag + " 对应的 structure !" |
| 451 | + + "非开放请求必须是后端 Request 表中校验规则允许的操作!如果需要则在 Request 表中新增配置!"); |
| 452 | + } |
| 453 | + |
| 454 | + //M clone 浅拷贝没用,Structure.parse 会导致 structure 里面被清空,第二次从缓存里取到的就是 {} |
| 455 | + verifier.verifyRequest(requestMethod, "", JSON.createJSONObject(target), rawReq, 0, null, null, creator); |
| 456 | + } |
| 457 | + |
| 458 | + M apijsonReq = JSON.parseObject(apijson); |
| 459 | + if (apijsonReq == null) { |
| 460 | + apijsonReq = JSON.createJSONObject(); |
| 461 | + } |
| 462 | + |
| 463 | + Set<Map.Entry<String, Object>> rawSet = rawReq.entrySet(); |
| 464 | + if (rawSet != null && rawSet.isEmpty() == false) { |
| 465 | + for (Map.Entry<String, Object> entry : rawSet) { |
| 466 | + String key = entry == null ? null : entry.getKey(); |
| 467 | + if (key == null) { // value 为 null 有效 |
| 468 | + continue; |
| 469 | + } |
| 470 | + |
| 471 | + String[] pathKeys = key.split("\\."); |
| 472 | + //逐层到达child的直接容器JSONObject parent |
| 473 | + int last = pathKeys.length - 1; |
| 474 | + M parent = apijsonReq; |
| 475 | + for (int i = 0; i < last; i++) {//一步一步到达指定位置 |
| 476 | + M p = getJSONObject(parent, pathKeys[i]); |
| 477 | + if (p == null) { |
| 478 | + p = JSON.createJSONObject(); |
| 479 | + parent.put(key, p); |
| 480 | + } |
| 481 | + parent = p; |
| 482 | + } |
| 483 | + |
| 484 | + parent.put(pathKeys[last], entry.getValue()); |
| 485 | + } |
| 486 | + } |
| 487 | + |
| 488 | + // 没必要,已经是预设好的实际参数了,如果要 tag 就在 apijson 字段配置 apijsonReq.put(JSONRequest.KEY_TAG, tag); |
| 489 | + |
| 490 | + return parser.setNeedVerifyContent(false).parse(apijsonReq); |
| 491 | + } |
| 492 | + catch (Exception e) { |
| 493 | + return JSON.toJSONString(parser.newErrorResult(e)); |
| 494 | + } |
| 495 | + } |
286 | 496 |
|
287 | 497 | //通用接口,非事务型操作 和 简单事务型操作 都可通过这些接口自动化实现>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
288 | 498 |
|
|
0 commit comments