APIJSON (thirteen: AbstractVerifier source code reading (4))

APIJSON (thirteen: AbstractVerifier source code reading (4))

2021SC@SDUSC

verifyRepeat

This method is to verify whether the duplicate

public void verifyRepeat(String table, String key, Object value, long exceptId) throws Exception

You will find that the parameters passed in by this method are table, key, value, and exceptId.

First, it will verify whether the key and value are empty at the same time

if (key == null || value == null) {
   Log.e(TAG, "verifyRepeat  key == null || value == null >> return;");
   return;
}

If they are all empty, an error will be reported.

After that, check the type of value

if (value instanceof JSON) {
   throw new UnsupportedDataTypeException(key + ":value 中value的类型不能为JSON!");
}

The type of value cannot be JSON

After that, a new JSONRequest class will be created according to the incoming key and value.

JSONRequest request = new JSONRequest(key, value);
if (exceptId > 0) {//允许修改自己的属性为该属性原来的值
   request.put(JSONRequest.KEY_ID + "!", exceptId);  // FIXME 这里 id 写死了,不支持自定义
}

At the same time, if exceptId > 0, JSONRequest.KEY_ID + "!", exceptId will be inserted into the request (if the key exists, it will be overwritten)

Then the JSONRequest class will be created with the corresponding method

JSONObject repeat = createParser().setMethod(HEAD).setNeedVerify(true).parseResponse(
      new JSONRequest(table, request)
      );

If the repeat is empty or the repeat has been repeated, an error will be reported

repeat = repeat == null ? null : repeat.getJSONObject(table);
if (repeat == null) {
   throw new Exception("服务器内部错误  verifyRepeat  repeat == null");
}
if (repeat.getIntValue(JSONResponse.KEY_COUNT) > 0) {
   throw new ConflictException(key + ": " + value + " 已经存在,不能重复!");
}

verifyRequest

After that, there are a large number of verifyRequest methods that extract the content specified by the target from the request, and their overloads.

@Override
public JSONObject verifyRequest(@NotNull final RequestMethod method, final String name
      , final JSONObject target, final JSONObject request, final int maxUpdateCount
      , final String database, final String schema, final SQLCreator creator) throws Exception {
   return verifyRequest(method, name, target, request, maxUpdateCount, database, schema, this, creator);
}

public static JSONObject verifyRequest(@NotNull final RequestMethod method, final String name
      , final JSONObject target, final JSONObject request, final SQLCreator creator) throws Exception {
   return verifyRequest(method, name, target, request, Parser.MAX_UPDATE_COUNT, creator);
}

public static JSONObject verifyRequest(@NotNull final RequestMethod method, final String name
      , final JSONObject target, final JSONObject request, final int maxUpdateCount, final SQLCreator creator) throws Exception {
   return verifyRequest(method, name, target, request, maxUpdateCount, null, null, null, creator);
}


public static JSONObject verifyRequest(@NotNull final RequestMethod method, final String name
      , final JSONObject target, final JSONObject request, final int maxUpdateCount
      , final String database, final String schema, final IdCallback idCallback, final SQLCreator creator) throws Exception {
   return verifyRequest(method, name, target, request, maxUpdateCount, database, schema, null, idCallback, creator);
}

public static JSONObject verifyRequest(@NotNull final RequestMethod method, final String name
      , final JSONObject target, final JSONObject request, final int maxUpdateCount
      , final String database, final String schema, final String datasource, final IdCallback idCallback, final SQLCreator creator) throws Exception {

   Log.i(TAG, "verifyRequest  method = " + method  + "; name = " + name
         + "; target = \n" + JSON.toJSONString(target)
         + "\n request = \n" + JSON.toJSONString(request));

   if (target == null || request == null) {// || request.isEmpty()) {
      Log.i(TAG, "verifyRequest  target == null || request == null >> return null;");
      return null;
   }

   //解析
   return parse(method, name, target, request, database, schema, idCallback, creator, new OnParseCallback() {

      @Override
      public JSONObject onParseJSONObject(String key, JSONObject tobj, JSONObject robj) throws Exception {
         //          Log.i(TAG, "verifyRequest.parse.onParseJSONObject  key = " + key + "; robj = " + robj);

         if (robj == null) {
            if (tobj != null) {//不允许不传Target中指定的Table
               throw new IllegalArgumentException(method + "请求,请在 " + name + " 内传 " + key + ":{} !");
            }
         } else if (apijson.JSONObject.isTableKey(key)) {
            String db = request.getString(apijson.JSONObject.KEY_DATABASE);
            String sh = request.getString(apijson.JSONObject.KEY_SCHEMA);
            String ds = request.getString(apijson.JSONObject.KEY_DATASOURCE);
            if (StringUtil.isEmpty(db, false)) {
               db = database;
            }
            if (StringUtil.isEmpty(sh, false)) {
               sh = schema;
            }
            if (StringUtil.isEmpty(ds, false)) {
               ds = datasource;
            }

            String idKey = idCallback == null ? null : idCallback.getIdKey(db, sh, ds, key);
            String finalIdKey = StringUtil.isEmpty(idKey, false) ? apijson.JSONObject.KEY_ID : idKey;

            if (method == RequestMethod.POST) {
               if (robj.containsKey(finalIdKey)) {
                  throw new IllegalArgumentException(method + "请求," + name + "/" + key + " 不能传 " + finalIdKey + " !");
               }
            } else {
               if (RequestMethod.isQueryMethod(method) == false) {
                  verifyId(method.name(), name, key, robj, finalIdKey, maxUpdateCount, true);

                  String userIdKey = idCallback == null ? null : idCallback.getUserIdKey(db, sh, ds, key);
                  String finalUserIdKey = StringUtil.isEmpty(userIdKey, false) ? apijson.JSONObject.KEY_USER_ID : userIdKey;
                  verifyId(method.name(), name, key, robj, finalUserIdKey, maxUpdateCount, false);
               }
            }
         }

         return verifyRequest(method, key, tobj, robj, maxUpdateCount, database, schema, idCallback, creator);
      }

      @Override
      protected JSONArray onParseJSONArray(String key, JSONArray tarray, JSONArray rarray) throws Exception {
         if ((method == RequestMethod.POST || method == RequestMethod.PUT) && JSONRequest.isArrayKey(key)) {
            if (rarray == null || rarray.isEmpty()) {
               throw new IllegalArgumentException(method + "请求,请在 " + name + " 内传 " + key + ":[{ ... }] "
                     + ",批量新增 Table[]:value 中 value 必须是包含表对象的非空数组!其中每个子项 { ... } 都是"
                     + " tag:" + key.substring(0, key.length() - 2) + " 对应单个新增的 structure !");
            }
            if (rarray.size() > maxUpdateCount) {
               throw new IllegalArgumentException(method + "请求," + name + "/" + key
                     + " 里面的 " + key + ":[{ ... }] 中 [] 的长度不能超过 " + maxUpdateCount + " !");
            }
         }
         return super.onParseJSONArray(key, tarray, rarray);
      }
   });

}

verifyId

Next is id verification for modify or delete operations

private static void verifyId(@NotNull String method, @NotNull String name, @NotNull String key
      , @NotNull JSONObject robj, @NotNull String idKey, final int maxUpdateCount, boolean atLeastOne) {
   //单个修改或删除
   Object id = robj.get(idKey); //如果必须传 id ,可在Request表中配置NECESSARY
   if (id != null && id instanceof Number == false && id instanceof String == false) {
      throw new IllegalArgumentException(method + "请求," + name + "/" + key
            + " 里面的 " + idKey + ":value 中value的类型只能是 Long 或 String !");
   }


   //批量修改或删除
   String idInKey = idKey + "{}";

   JSONArray idIn = null;
   try {
      idIn = robj.getJSONArray(idInKey); //如果必须传 id{} ,可在Request表中配置NECESSARY
   } catch (Exception e) {
      throw new IllegalArgumentException(method + "请求," + name + "/" + key
            + " 里面的 " + idInKey + ":value 中value的类型只能是 [Long] !");
   }
   if (idIn == null) {
      if (atLeastOne && id == null) {
         throw new IllegalArgumentException(method + "请求," + name + "/" + key
               + " 里面 " + idKey + " 和 " + idInKey + " 至少传其中一个!");
      }
   } else {
      if (idIn.size() > maxUpdateCount) { //不允许一次操作 maxUpdateCount 条以上记录
         throw new IllegalArgumentException(method + "请求," + name + "/" + key
               + " 里面的 " + idInKey + ":[] 中[]的长度不能超过 " + maxUpdateCount + " !");
      }
      //解决 id{}: ["1' OR 1='1'))--"] 绕过id{}限制
      //new ArrayList<Long>(idIn) 不能检查类型,Java泛型擦除问题,居然能把 ["a"] 赋值进去还不报错
      for (int i = 0; i < idIn.size(); i++) {
         Object o = idIn.get(i);
         if (o == null) {
            throw new IllegalArgumentException(method + "请求," + name + "/" + key
                  + " 里面的 " + idInKey + ":[] 中所有项都不能为 [ null, <= 0 的数字, 空字符串 \"\" ] 中任何一个 !");
         }
         if (o instanceof Number) {
            //解决 Windows mysql-5.6.26-winx64 等低于 5.7 的 MySQL 可能 id{}: [0] 生成 id IN(0) 触发 MySQL bug 导致忽略 IN 条件
            //例如 UPDATE `apijson`.`TestRecord` SET `testAccountId` = -1 WHERE ( (`id` IN (0)) AND (`userId`= 82001) )
            if (((Number) o).longValue() <= 0) {
               throw new IllegalArgumentException(method + "请求," + name + "/" + key
                     + " 里面的 " + idInKey + ":[] 中所有项都不能为 [ null, <= 0 的数字, 空字符串 \"\" ] 中任何一个 !");
            }
         }
         else if (o instanceof String) {
            if (StringUtil.isEmpty(o, true)) {
               throw new IllegalArgumentException(method + "请求," + name + "/" + key
                     + " 里面的 " + idInKey + ":[] 中所有项都不能为 [ null, <= 0 的数字, 空字符串 \"\" ] 中任何一个 !");
            }
         }
         else {
            throw new IllegalArgumentException(method + "请求," + name + "/" + key
                  + " 里面的 " + idInKey + ":[] 中所有项的类型都只能是 Long 或 String !");
         }
      }
   }
}

verifyResponse

The verifyResponse method is to verify and convert the response to the specified content and structure

@Override
public JSONObject verifyResponse(@NotNull final RequestMethod method, final String name
      , final JSONObject target, final JSONObject response, final String database, final String schema
      , SQLCreator creator, OnParseCallback callback) throws Exception {
   return verifyResponse(method, name, target, response, database, schema, this, creator, callback);
}

public static JSONObject verifyResponse(@NotNull final RequestMethod method, final String name
      , final JSONObject target, final JSONObject response, SQLCreator creator, OnParseCallback callback) throws Exception {
   return verifyResponse(method, name, target, response, null, null, null, creator, callback);
}

public static JSONObject verifyResponse(@NotNull final RequestMethod method, final String name
      , final JSONObject target, final JSONObject response, final String database, final String schema
      , final IdCallback idKeyCallback, SQLCreator creator, OnParseCallback callback) throws Exception {

   Log.i(TAG, "verifyResponse  method = " + method  + "; name = " + name
         + "; target = \n" + JSON.toJSONString(target)
         + "\n response = \n" + JSON.toJSONString(response));

   if (target == null || response == null) {// || target.isEmpty() {
      Log.i(TAG, "verifyResponse  target == null || response == null >> return response;");
      return response;
   }

   //解析
   return parse(method, name, target, response, database, schema, idKeyCallback, creator, callback != null ? callback : new OnParseCallback() {
      @Override
      protected JSONObject onParseJSONObject(String key, JSONObject tobj, JSONObject robj) throws Exception {
         return verifyResponse(method, key, tobj, robj, database, schema, idKeyCallback, creator, callback);
      }
   });
}

parse

Parse method - use callback to return different parsing of request and response

public static JSONObject parse(@NotNull final RequestMethod method, String name, JSONObject target, JSONObject real
      , SQLCreator creator, @NotNull OnParseCallback callback) throws Exception {
   return parse(method, name, target, real, null, null, null, creator, callback);
}

public static JSONObject parse(@NotNull final RequestMethod method, String name, JSONObject target, JSONObject real
      , final String database, final String schema, final IdCallback idCallback, SQLCreator creator, @NotNull OnParseCallback callback) throws Exception {
   return parse(method, name, target, real, database, schema, null, idCallback, creator, callback);
}

public static JSONObject parse(@NotNull final RequestMethod method, String name, JSONObject target, JSONObject real
      , final String database, final String schema, final String datasource, final IdCallback idCallback, SQLCreator creator, @NotNull OnParseCallback callback) throws Exception {
   if (target == null) {
      return null;
   }

   // 获取配置<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   JSONObject type = target.getJSONObject(TYPE.name());
   JSONObject verify = target.getJSONObject(VERIFY.name());
   JSONObject insert = target.getJSONObject(INSERT.name());
   JSONObject update = target.getJSONObject(UPDATE.name());
   JSONObject replace = target.getJSONObject(REPLACE.name());

   String exist = StringUtil.getNoBlankString(target.getString(EXIST.name()));
   String unique = StringUtil.getNoBlankString(target.getString(UNIQUE.name()));
   String remove = StringUtil.getNoBlankString(target.getString(REMOVE.name()));
   String must = StringUtil.getNoBlankString(target.getString(MUST.name()));
   String refuse = StringUtil.getNoBlankString(target.getString(REFUSE.name()));


   // 移除字段<<<<<<<<<<<<<<<<<<<
   String[] removes = StringUtil.split(remove);
   if (removes != null && removes.length > 0) {
      for (String r : removes) {
         real.remove(r);
      }
   }
   // 移除字段>>>>>>>>>>>>>>>>>>>

   // 判断必要字段是否都有<<<<<<<<<<<<<<<<<<<
   String[] musts = StringUtil.split(must);
   List<String> mustList = musts == null ? new ArrayList<String>() : Arrays.asList(musts);
   for (String s : mustList) {
      if (real.get(s) == null) {  // 可能传null进来,这里还会通过 real.containsKey(s) == false) {
         throw new IllegalArgumentException(method + "请求," + name
               + " 里面不能缺少 " + s + " 等[" + must + "]内的任何字段!");
      }
   }
   //判断必要字段是否都有>>>>>>>>>>>>>>>>>>>


   Set<String> objKeySet = new HashSet<String>(); //不能用tableKeySet,仅判断 Table:{} 会导致 key:{ Table:{} } 绕过判断

   //解析内容<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

   Set<Map.Entry<String, Object>> set = new LinkedHashSet<>(target.entrySet());
   if (set.isEmpty() == false) {

      String key;
      Object tvalue;
      Object rvalue;
      for (Map.Entry<String, Object> entry : set) {
         key = entry == null ? null : entry.getKey();
         if (key == null || OPERATION_KEY_LIST.contains(key)) {
            continue;
         }
         tvalue = entry.getValue();
         rvalue = real.get(key);
         if (callback.onParse(key, tvalue, rvalue) == false) {
            continue;
         }

         if (tvalue instanceof JSONObject) { //JSONObject,往下一级提取
            if (rvalue != null && rvalue instanceof JSONObject == false) {
               throw new UnsupportedDataTypeException(key + ":value 的value不合法!类型必须是 OBJECT ,结构为 {} !");
            }
            tvalue = callback.onParseJSONObject(key, (JSONObject) tvalue, (JSONObject) rvalue);

            objKeySet.add(key);
         } else if (tvalue instanceof JSONArray) { //JSONArray
            if (rvalue != null && rvalue instanceof JSONArray == false) {
               throw new UnsupportedDataTypeException(key + ":value 的value不合法!类型必须是 ARRAY ,结构为 [] !");
            }
            tvalue = callback.onParseJSONArray(key, (JSONArray) tvalue, (JSONArray) rvalue);

            if ((method == RequestMethod.POST || method == RequestMethod.PUT) && JSONRequest.isArrayKey(key)) {
               objKeySet.add(key);
            }
         } else {//其它Object
            tvalue = callback.onParseObject(key, tvalue, rvalue);
         }

         if (tvalue != null) {//可以在target中加上一些不需要客户端传的键值对
            real.put(key, tvalue);
         }
      }

   }

   //解析内容>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>



   Set<String> rkset = real.keySet(); //解析内容并没有改变rkset

   //解析不允许的字段<<<<<<<<<<<<<<<<<<<
   List<String> refuseList = new ArrayList<String>();
   if ("!".equals(refuse)) {//所有非 must,改成 !must 更好
      for (String key : rkset) {//对@key放行,@role,@column,自定义@position等
         if (key != null && key.startsWith("@") == false
               && mustList.contains(key) == false && objKeySet.contains(key) == false) {
            refuseList.add(key);
         }
      }
   } else {
      String[] refuses = StringUtil.split(refuse);
      if (refuses != null && refuses.length > 0) {
         refuseList.addAll(Arrays.asList(refuses));
      }
   }
   //解析不允许的字段>>>>>>>>>>>>>>>>>>>


   //判断不允许传的key<<<<<<<<<<<<<<<<<<<<<<<<<
   for (String rk : rkset) {
      if (refuseList.contains(rk)) { //不允许的字段
         throw new IllegalArgumentException(method + "请求," + name
               + " 里面不允许传 " + rk + " 等" + StringUtil.getString(refuseList) + "内的任何字段!");
      }

      if (rk == null) { //无效的key
         real.remove(rk);
         continue;
      }

      Object rv = real.get(rk);

      //不允许传远程函数,只能后端配置
      if (rk.endsWith("()") && rv instanceof String) {
         throw new UnsupportedOperationException(method + " 请求," + rk + " 不合法!非开放请求不允许传远程函数 key():\"fun()\" !");
      }

      //不在target内的 key:{}
      if (rk.startsWith("@") == false && objKeySet.contains(rk) == false) {
         if (rv instanceof JSONObject) {
            throw new UnsupportedOperationException(method + " 请求," +name + " 里面不允许传 " + rk + ":{} !");
         }
         if ((method == RequestMethod.POST || method == RequestMethod.PUT) && rv instanceof JSONArray && JSONRequest.isArrayKey(rk)) {
            throw new UnsupportedOperationException(method + " 请求," + name + " 里面不允许 " + rk + ":[] 等未定义的 Table[]:[{}] 批量操作键值对!");
         }
      }
   }
   //判断不允许传的key>>>>>>>>>>>>>>>>>>>>>>>>>



   //校验与修改Request<<<<<<<<<<<<<<<<<
   //在tableKeySet校验后操作,避免 导致put/add进去的Table 被当成原Request的内容
   real = operate(TYPE, type, real, creator);
   real = operate(VERIFY, verify, real, creator);
   real = operate(INSERT, insert, real, creator);
   real = operate(UPDATE, update, real, creator);
   real = operate(REPLACE, replace, real, creator);
   //校验与修改Request>>>>>>>>>>>>>>>>>


   String db = real.getString(apijson.JSONObject.KEY_DATABASE);
   String sh = real.getString(apijson.JSONObject.KEY_SCHEMA);
   String ds = real.getString(apijson.JSONObject.KEY_DATASOURCE);
   if (StringUtil.isEmpty(db, false)) {
      db = database;
   }
   if (StringUtil.isEmpty(sh, false)) {
      sh = schema;
   }
   if (StringUtil.isEmpty(ds, false)) {
      ds = datasource;
   }
   String idKey = idCallback == null ? null : idCallback.getIdKey(db, sh, ds, name);
   String finalIdKey = StringUtil.isEmpty(idKey, false) ? apijson.JSONObject.KEY_ID : idKey;

   //TODO放在operate前?考虑性能、operate修改后再验证的值是否和原来一样
   //校验存在<<<<<<<<<<<<<<<<<<< TODO 格式改为 id;version,tag 兼容多个字段联合主键
   String[] exists = StringUtil.split(exist);
   if (exists != null && exists.length > 0) {
      long exceptId = real.getLongValue(finalIdKey);
      for (String e : exists) {
         verifyExist(name, e, real.get(e), exceptId, creator);
      }
   }
   //校验存在>>>>>>>>>>>>>>>>>>>

   //TODO放在operate前?考虑性能、operate修改后再验证的值是否和原来一样
   //校验重复<<<<<<<<<<<<<<<<<<< TODO 格式改为 id;version,tag 兼容多个字段联合主键
   String[] uniques = StringUtil.split(unique);
   if (uniques != null && uniques.length > 0) {
      long exceptId = real.getLongValue(finalIdKey);
      for (String u : uniques) {
         verifyRepeat(name, u, real.get(u), exceptId, finalIdKey, creator);
      }
   }
   //校验重复>>>>>>>>>>>>>>>>>>>


   Log.i(TAG, "parse  return real = " + JSON.toJSONString(real));
   return real;
}
虑性能、operate修改后再验证的值是否和原来一样
   //校验重复<<<<<<<<<<<<<<<<<<< TODO 格式改为 id;version,tag 兼容多个字段联合主键
   String[] uniques = StringUtil.split(unique);
   if (uniques != null && uniques.length > 0) {
      long exceptId = real.getLongValue(finalIdKey);
      for (String u : uniques) {
         verifyRepeat(name, u, real.get(u), exceptId, finalIdKey, creator);
      }
   }
   //校验重复>>>>>>>>>>>>>>>>>>>


   Log.i(TAG, "parse  return real = " + JSON.toJSONString(real));
   return real;
}

Guess you like

Origin blog.csdn.net/qq_50861917/article/details/122159112