APIJSON(十三:AbstractVerifier源码阅读(4))

APIJSON(十三:AbstractVerifier源码阅读(4))

2021SC@SDUSC

verifyRepeat

此方法是验证是否重复

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

会发现该方法传入的参数有table、key、value、exceptId。

首先会验证key和value是否同时为空

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

都为空就会报错

之后是查看value值的类型

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

value的类型不能为JSON

之后会根据传入的key、value新建一个JSONRequest类。

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

同时如果exceptId > 0,就会将JSONRequest.KEY_ID + “!”,exceptId插入request中(如果存在该key就会进行覆盖)

之后会用对应的方法创建JSONRequest类

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

如果repeat为空或者repeat已重复,就会报错

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

之后是大量的从request提取target指定的内容的verifyRequest方法,及对其的重载。

@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

接下来是对修改或删除操作的id验证

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

verifyResponse方法是校验并将response转换为指定的内容和结构

@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方法——对request和response不同的解析用callback返回

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;
}

猜你喜欢

转载自blog.csdn.net/qq_50861917/article/details/122159112