From c32498b04162d19530c00b1cdf41bc958f3dbdee Mon Sep 17 00:00:00 2001 From: TommyLemon <1184482681@qq.com> Date: Sun, 22 Nov 2020 02:26:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=93=8D=E4=BD=9C=E6=96=B9=E6=B3=95=20Operatio?= =?UTF-8?q?n=20=E6=96=B0=E5=A2=9E=20MUST=20=E5=92=8C=20REFUSE=20=E5=88=86?= =?UTF-8?q?=E5=88=AB=E6=9B=BF=E4=BB=A3=20NECESSARY=20=E5=92=8C=20DISALLOW?= =?UTF-8?q?=EF=BC=9B=E8=A7=A3=E5=86=B3=20Structure.sqlVerify=20=E4=B8=8D?= =?UTF-8?q?=E5=8F=AF=E7=94=A8=E5=8F=8A=E9=A2=84=E9=98=B2=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E7=9A=84=20SQL=20=E6=B3=A8=E5=85=A5=EF=BC=9B=E8=A7=A3=E5=86=B3?= =?UTF-8?q?=20SQLConfig=20=E8=87=AA=E5=AE=9A=E4=B9=89=E7=9A=84=20idKey=20?= =?UTF-8?q?=E5=92=8C=20userIdKey=20=E5=9C=A8=20Structure=20=E4=B8=AD?= =?UTF-8?q?=E6=9C=AA=E5=90=8C=E6=AD=A5=E5=AF=BC=E8=87=B4=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E5=80=BC=E6=A0=A1=E9=AA=8C=E4=B8=8D=E9=80=9A=E8=BF=87?= =?UTF-8?q?=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/apijson/orm/AbstractParser.java | 21 +- .../java/apijson/orm/AbstractSQLConfig.java | 68 ++++-- .../src/main/java/apijson/orm/Operation.java | 21 +- .../src/main/java/apijson/orm/Structure.java | 203 +++++++++++++++--- .../src/main/java/apijson/orm/model/Test.java | 4 +- 5 files changed, 259 insertions(+), 58 deletions(-) diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java index 64ebd2992..331b542e5 100755 --- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java +++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java @@ -34,6 +34,7 @@ import apijson.RequestMethod; import apijson.RequestRole; import apijson.StringUtil; +import apijson.orm.AbstractSQLConfig.IdCallback; import apijson.orm.exception.ConditionErrorException; import apijson.orm.exception.ConflictException; import apijson.orm.exception.NotExistException; @@ -43,7 +44,7 @@ /**parser for parsing request to JSONObject * @author Lemon */ -public abstract class AbstractParser implements Parser, ParserCreator, VerifierCreator, SQLCreator { +public abstract class AbstractParser implements Parser, ParserCreator, VerifierCreator, SQLCreator, IdCallback { protected static final String TAG = "AbstractParser"; @@ -490,11 +491,25 @@ public JSONObject parseCorrectRequest(RequestMethod method, String tag, int vers //获取指定的JSON结构 >>>>>>>>>>>>>> + //JSONObject clone 浅拷贝没用,Structure.parse 会导致 structure 里面被清空,第二次从缓存里取到的就是 {} - return Structure.parseRequest(method, name, target, request, maxUpdateCount, creator); + return Structure.parseRequest(method, name, target, request, maxUpdateCount, getGlobleDatabase(), getGlobleSchema(), this, creator); } - + @Override + public String getIdKey(String database, String schema, String table) { + return apijson.JSONObject.KEY_ID; + } + @Override + public String getUserIdKey(String database, String schema, String table) { + return apijson.JSONObject.KEY_USER_ID; + } + @Override + public Object newId(RequestMethod method, String database, String schema, String table) { + return System.currentTimeMillis(); + } + + /**新建带状态内容的JSONObject * @param code * @param msg diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java index c2bdf0933..892e87719 100755 --- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java +++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java @@ -16,8 +16,8 @@ import static apijson.JSONObject.KEY_ID; import static apijson.JSONObject.KEY_JSON; import static apijson.JSONObject.KEY_ORDER; -import static apijson.JSONObject.KEY_ROLE; import static apijson.JSONObject.KEY_RAW; +import static apijson.JSONObject.KEY_ROLE; import static apijson.JSONObject.KEY_SCHEMA; import static apijson.JSONObject.KEY_USER_ID; import static apijson.RequestMethod.DELETE; @@ -48,6 +48,7 @@ import com.alibaba.fastjson.annotation.JSONField; import apijson.JSON; +import apijson.JSONResponse; import apijson.Log; import apijson.NotNull; import apijson.RequestMethod; @@ -55,13 +56,20 @@ import apijson.SQL; import apijson.StringUtil; import apijson.orm.exception.NotExistException; +import apijson.orm.model.Access; import apijson.orm.model.Column; +import apijson.orm.model.Document; import apijson.orm.model.ExtendedProperty; +import apijson.orm.model.Function; import apijson.orm.model.PgAttribute; import apijson.orm.model.PgClass; +import apijson.orm.model.Request; +import apijson.orm.model.Response; import apijson.orm.model.SysColumn; import apijson.orm.model.SysTable; import apijson.orm.model.Table; +import apijson.orm.model.Test; +import apijson.orm.model.TestRecord; /**config sql for JSON Request * @author Lemon @@ -77,6 +85,7 @@ public abstract class AbstractSQLConfig implements SQLConfig { * 表名映射,隐藏真实表名,对安全要求很高的表可以这么做 */ public static final Map TABLE_KEY_MAP; + public static final List CONFIG_TABLE_LIST; public static final List DATABASE_LIST; // 自定义where条件拼接 public static final Map RAW_MAP; @@ -89,6 +98,16 @@ public abstract class AbstractSQLConfig implements SQLConfig { TABLE_KEY_MAP.put(SysTable.class.getSimpleName(), SysTable.TABLE_NAME); TABLE_KEY_MAP.put(SysColumn.class.getSimpleName(), SysColumn.TABLE_NAME); TABLE_KEY_MAP.put(ExtendedProperty.class.getSimpleName(), ExtendedProperty.TABLE_NAME); + + CONFIG_TABLE_LIST = new ArrayList<>(); // Table, Column 等是系统表 AbstractVerifier.SYSTEM_ACCESS_MAP.keySet()); + CONFIG_TABLE_LIST.add(Function.class.getSimpleName()); + CONFIG_TABLE_LIST.add(Request.class.getSimpleName()); + CONFIG_TABLE_LIST.add(Response.class.getSimpleName()); + CONFIG_TABLE_LIST.add(Test.class.getSimpleName()); + CONFIG_TABLE_LIST.add(Access.class.getSimpleName()); + CONFIG_TABLE_LIST.add(Document.class.getSimpleName()); + CONFIG_TABLE_LIST.add(TestRecord.class.getSimpleName()); + DATABASE_LIST = new ArrayList<>(); DATABASE_LIST.add(DATABASE_MYSQL); @@ -1623,8 +1642,18 @@ public String getCompareString(String key, Object value, String type) throws Exc } public String getKey(String key) { + if (isTest()) { + if (key.contains("'")) { // || key.contains("#") || key.contains("--")) { + throw new IllegalArgumentException("参数 " + key + " 不合法!key 中不允许有单引号 ' !"); + } + return getSQLValue(key).toString(); + } + + return getSQLKey(key); + } + public String getSQLKey(String key) { String q = getQuote(); - return (isKeyPrefix() ? getAliasWithQuote() + "." : "") + q + key + q; + return (isKeyPrefix() ? getAliasWithQuote() + "." : "") + q + key + q; } /** @@ -1636,6 +1665,9 @@ private Object getValue(@NotNull Object value) { preparedValueList.add(value); return "?"; } + return getSQLValue(value); + } + public Object getSQLValue(@NotNull Object value) { // return (value instanceof Number || value instanceof Boolean) && DATABASE_POSTGRESQL.equals(getDatabase()) ? value : "'" + value + "'"; return (value instanceof Number || value instanceof Boolean) ? value : "'" + value + "'"; //MySQL 隐式转换用不了索引 } @@ -2230,9 +2262,15 @@ public static String getSQL(AbstractSQLConfig config) throws Exception { case DELETE: return "DELETE FROM " + tablePath + config.getWhereString(true); default: + String explain = (config.isExplain() ? (config.isSQLServer() || config.isOracle() ? "SET STATISTICS PROFILE ON " : "EXPLAIN ") : ""); + if (config.isTest() && RequestMethod.isGetMethod(config.getMethod(), true)) { + String q = config.getQuote(); // 生成 SELECT ( (24 >=0 AND 24 <3) ) AS `code` LIMIT 1 OFFSET 0 + return explain + "SELECT " + config.getWhereString(false) + " AS " + q + JSONResponse.KEY_CODE + q + config.getLimitString(); + } + config.setPreparedValueList(new ArrayList()); String column = config.getColumnString(); - return (config.isExplain() ? (config.isSQLServer() || config.isOracle() ? "SET STATISTICS PROFILE ON " : "EXPLAIN ") : "") + "SELECT " + (config.getCache() == JSONRequest.CACHE_RAM ? "SQL_NO_CACHE " : "") + column + " FROM " + getConditionString(column, tablePath, config); + return explain + "SELECT " + (config.getCache() == JSONRequest.CACHE_RAM ? "SQL_NO_CACHE " : "") + column + " FROM " + getConditionString(column, tablePath, config); } } @@ -2915,16 +2953,7 @@ else if (key.endsWith("-")) {//缩减,PUT查询时处理 } - public static interface Callback { - /**获取 SQLConfig 的实例 - * @param method - * @param database - * @param schema - * @param table - * @return - */ - SQLConfig getSQLConfig(RequestMethod method, String database, String schema, String table); - + public static interface IdCallback { /**为 post 请求新建 id, 只能是 Long 或 String * @param method * @param database @@ -2949,7 +2978,18 @@ public static interface Callback { * @return */ String getUserIdKey(String database, String schema, String table); - + } + + public static interface Callback extends IdCallback { + /**获取 SQLConfig 的实例 + * @param method + * @param database + * @param schema + * @param table + * @return + */ + SQLConfig getSQLConfig(RequestMethod method, String database, String schema, String table); + /**combine 里的 key 在 request 中 value 为 null 或不存在,即 request 中缺少用来作为 combine 条件的 key: value * @param combine * @param key diff --git a/APIJSONORM/src/main/java/apijson/orm/Operation.java b/APIJSONORM/src/main/java/apijson/orm/Operation.java index ec93b0300..a0e2278b1 100755 --- a/APIJSONORM/src/main/java/apijson/orm/Operation.java +++ b/APIJSONORM/src/main/java/apijson/orm/Operation.java @@ -9,21 +9,26 @@ * @author Lemon */ public enum Operation { + /** + * 必须传的字段,结构是 + * "key0,key1,key2..." + */ + MUST, + /** + * @deprecated 用 MUST 代替,最早可能 4.5.0 移除 + */ + NECESSARY, /** * 不允许传的字段,结构是 * "key0,key1,key2..." - * TODO 改成 MUST 减少长度 ? */ - DISALLOW, - + REFUSE, /** - * 必须传的字段,结构是 - * "key0,key1,key2..." - * TODO 改成 REFUSE 减少长度 ? + * @deprecated 用 REFUSE 代替,最早可能 4.5.0 移除 */ - NECESSARY, - + DISALLOW, + /**TODO 是否应该把数组类型写成 BOOLEANS, NUMBERS 等复数单词,以便抽取 enum ?扩展用 VERIFY 或 INSERT/UPDATE 远程函数等 * 验证是否符合预设的类型: diff --git a/APIJSONORM/src/main/java/apijson/orm/Structure.java b/APIJSONORM/src/main/java/apijson/orm/Structure.java index 4b752de08..9f7d622d6 100755 --- a/APIJSONORM/src/main/java/apijson/orm/Structure.java +++ b/APIJSONORM/src/main/java/apijson/orm/Structure.java @@ -5,12 +5,12 @@ package apijson.orm; -import static apijson.JSONObject.KEY_ID; -import static apijson.JSONObject.KEY_USER_ID; import static apijson.orm.Operation.DISALLOW; import static apijson.orm.Operation.EXIST; import static apijson.orm.Operation.INSERT; +import static apijson.orm.Operation.MUST; import static apijson.orm.Operation.NECESSARY; +import static apijson.orm.Operation.REFUSE; import static apijson.orm.Operation.REMOVE; import static apijson.orm.Operation.REPLACE; import static apijson.orm.Operation.TYPE; @@ -45,10 +45,11 @@ import apijson.NotNull; import apijson.RequestMethod; import apijson.StringUtil; +import apijson.orm.AbstractSQLConfig.Callback; +import apijson.orm.AbstractSQLConfig.IdCallback; import apijson.orm.exception.ConflictException; -import apijson.orm.model.Test; -/**结构类 +/**结构类。 TODO 重构为 AbstractContentVerifier implements ContentVerifier 或干脆整合进 AbstractVerifier * 增删改查: OPERATION(ADD,REPLACE,PUT,REMOVE) OPERATION:{key0:value0, key1:value1 ...} * 对值校验: VERIFY:{key0:value0, key1:value1 ...} (key{}:range,key$:"%m%"等) * 对值重复性校验: UNIQUE:"key0:, key1 ..." (UNIQUE:"phone,email" 等) @@ -62,7 +63,7 @@ public class Structure { COMPILE_MAP = new HashMap(); } - + private Structure() {} @@ -91,9 +92,28 @@ public static JSONObject parseRequest(@NotNull final RequestMethod method, final */ public static JSONObject parseRequest(@NotNull final RequestMethod method, final String name , final JSONObject target, final JSONObject request, final int maxUpdateCount, final SQLCreator creator) throws Exception { + return parseRequest(method, name, target, request, maxUpdateCount, null, null, null, creator); + } + /**从request提取target指定的内容 + * @param method + * @param name + * @param target + * @param request + * @param maxUpdateCount + * @param idKey + * @param userIdKey + * @param creator + * @return + * @throws Exception + */ + public static JSONObject parseRequest(@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 { + Log.i(TAG, "parseRequest 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, "parseRequest target == null || request == null >> return null;"); return null; @@ -105,30 +125,47 @@ public static JSONObject parseRequest(@NotNull final RequestMethod method, final // ":{ " + JSONRequest.KEY_ROLE + ":admin } !"); // } + //解析 - return parse(method, name, target, request, creator, new OnParseCallback() { + 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, "parseRequest.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); + if (StringUtil.isEmpty(db, false)) { + db = database; + } + if (StringUtil.isEmpty(sh, false)) { + sh = schema; + } + + String idKey = idCallback == null ? null : idCallback.getIdKey(db, sh, key); + String finalIdKey = StringUtil.isEmpty(idKey, false) ? apijson.JSONObject.KEY_ID : idKey; + if (method == RequestMethod.POST) { - if (robj.containsKey(KEY_ID)) { - throw new IllegalArgumentException(method + "请求," + name + "/" + key + " 不能传 " + KEY_ID + " !"); + if (robj.containsKey(finalIdKey)) { + throw new IllegalArgumentException(method + "请求," + name + "/" + key + " 不能传 " + finalIdKey + " !"); } } else { if (RequestMethod.isQueryMethod(method) == false) { - verifyId(method.name(), name, key, robj, KEY_ID, maxUpdateCount, true); - verifyId(method.name(), name, key, robj, KEY_USER_ID, maxUpdateCount, false); + verifyId(method.name(), name, key, robj, finalIdKey, maxUpdateCount, true); + + String userIdKey = idCallback == null ? null : idCallback.getUserIdKey(db, sh, key); + String finalUserIdKey = StringUtil.isEmpty(userIdKey, false) ? apijson.JSONObject.KEY_USER_ID : userIdKey; + verifyId(method.name(), name, key, robj, finalUserIdKey, maxUpdateCount, false); } } } - return parseRequest(method, key, tobj, robj, maxUpdateCount, creator); + return parseRequest(method, key, tobj, robj, maxUpdateCount, database, schema, idCallback, creator); } @Override @@ -214,34 +251,70 @@ private static void verifyId(@NotNull String method, @NotNull String name, @NotN */ public static JSONObject parseResponse(@NotNull final RequestMethod method, final String name , final JSONObject target, final JSONObject response, SQLCreator creator, OnParseCallback callback) throws Exception { + return parseResponse(method, name, target, response, null, null, null, creator, callback); + } + /**校验并将response转换为指定的内容和结构 + * @param method + * @param name + * @param target + * @param response + * @param idKey + * @param callback + * @param creator + * @return + * @throws Exception + */ + public static JSONObject parseResponse(@NotNull final RequestMethod method, final String name + , final JSONObject target, final JSONObject response, final String database, final String schema + , final Callback idKeyCallback, SQLCreator creator, OnParseCallback callback) throws Exception { + Log.i(TAG, "parseResponse 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, "parseRequest target == null || response == null >> return response;"); return response; } //解析 - return parse(method, name, target, response, creator, callback != null ? callback : new OnParseCallback() {}); + return parse(method, name, target, response, database, schema, idKeyCallback, creator, callback != null ? callback : new OnParseCallback() {}); } /**对request和response不同的解析用callback返回 + * @param method + * @param name * @param target - * @param request + * @param real + * @param creator * @param callback - * @param creator * @return - * @throws Exception + * @throws Exception */ 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); + } + + /**对request和response不同的解析用callback返回 + * @param method + * @param name + * @param target + * @param real + * @param idKey + * @param userIdKey + * @param creator + * @param callback + * @return + * @throws Exception + */ + 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 { if (target == null) { return null; } - //获取配置<<<<<<<<<<<<<<<<<<<<<<<<<<<< JSONObject type = target.getJSONObject(TYPE.name()); JSONObject verify = target.getJSONObject(VERIFY.name()); @@ -252,6 +325,8 @@ public static JSONObject parse(@NotNull final RequestMethod method, String 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 necessary = StringUtil.getNoBlankString(target.getString(NECESSARY.name())); String disallow = StringUtil.getNoBlankString(target.getString(DISALLOW.name())); @@ -264,6 +339,8 @@ public static JSONObject parse(@NotNull final RequestMethod method, String name, target.remove(EXIST.name()); target.remove(UNIQUE.name()); target.remove(REMOVE.name()); + target.remove(MUST.name()); + target.remove(REFUSE.name()); target.remove(NECESSARY.name()); target.remove(DISALLOW.name()); @@ -279,6 +356,15 @@ public static JSONObject parse(@NotNull final RequestMethod method, String name, //移除字段>>>>>>>>>>>>>>>>>>> //判断必要字段是否都有<<<<<<<<<<<<<<<<<<< + String[] musts = StringUtil.split(must); + List mustList = musts == null ? new ArrayList() : Arrays.asList(musts); + for (String s : mustList) { + if (real.get(s) == null) {//可能传null进来,这里还会通过 real.containsKey(s) == false) { + throw new IllegalArgumentException(method + "请求," + name + + " 里面不能缺少 " + s + " 等[" + must + "]内的任何字段!"); + } + } + String[] necessarys = StringUtil.split(necessary); List necessaryList = necessarys == null ? new ArrayList() : Arrays.asList(necessarys); for (String s : necessaryList) { @@ -323,7 +409,7 @@ public static JSONObject parse(@NotNull final RequestMethod method, String name, 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); } @@ -345,6 +431,21 @@ public static JSONObject parse(@NotNull final RequestMethod method, String name, Set rkset = real.keySet(); //解析内容并没有改变rkset //解析不允许的字段<<<<<<<<<<<<<<<<<<< + List refuseList = new ArrayList(); + if ("!".equals(refuse)) {//所有非 must,改成 !must 更好 + for (String key : rkset) {//对@key放行,@role,@column,自定义@position等 + if (key != null && key.startsWith("@") == false + && necessaryList.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)); + } + } + List disallowList = new ArrayList(); if ("!".equals(disallow)) {//所有非necessary,改成 !necessary 更好 for (String key : rkset) {//对@key放行,@role,@column,自定义@position等 @@ -364,6 +465,10 @@ public static JSONObject parse(@NotNull final RequestMethod method, String name, //判断不允许传的key<<<<<<<<<<<<<<<<<<<<<<<<< for (String rk : rkset) { + if (refuseList.contains(rk)) { //不允许的字段 + throw new IllegalArgumentException(method + "请求," + name + + " 里面不允许传 " + rk + " 等" + StringUtil.getString(refuseList) + "内的任何字段!"); + } if (disallowList.contains(rk)) { //不允许的字段 throw new IllegalArgumentException(method + "请求," + name + " 里面不允许传 " + rk + " 等" + StringUtil.getString(disallowList) + "内的任何字段!"); @@ -378,7 +483,7 @@ public static JSONObject parse(@NotNull final RequestMethod method, String name, //不允许传远程函数,只能后端配置 if (rk.endsWith("()") && rv instanceof String) { - throw new UnsupportedOperationException(method + " 请求," +rk + " 不合法!非开放请求不允许传远程函数 key():\"fun()\" !"); + throw new UnsupportedOperationException(method + " 请求," + rk + " 不合法!非开放请求不允许传远程函数 key():\"fun()\" !"); } //不在target内的 key:{} @@ -404,11 +509,23 @@ public static JSONObject parse(@NotNull final RequestMethod method, String name, real = operate(REPLACE, replace, real, creator); //校验与修改Request>>>>>>>>>>>>>>>>> + + String db = real.getString(apijson.JSONObject.KEY_DATABASE); + String sh = real.getString(apijson.JSONObject.KEY_SCHEMA); + if (StringUtil.isEmpty(db, false)) { + db = database; + } + if (StringUtil.isEmpty(sh, false)) { + sh = schema; + } + String idKey = idCallback == null ? null : idCallback.getIdKey(db, sh, 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(KEY_ID); + long exceptId = real.getLongValue(finalIdKey); for (String e : exists) { verifyExist(name, e, real.get(e), exceptId, creator); } @@ -419,9 +536,9 @@ public static JSONObject parse(@NotNull final RequestMethod method, String name, //校验重复<<<<<<<<<<<<<<<<<<< TODO 格式改为 id;version,tag 兼容多个字段联合主键 String[] uniques = StringUtil.split(unique); if (uniques != null && uniques.length > 0) { - long exceptId = real.getLongValue(KEY_ID); + long exceptId = real.getLongValue(finalIdKey); for (String u : uniques) { - verifyRepeat(name, u, real.get(u), exceptId, creator); + verifyRepeat(name, u, real.get(u), exceptId, finalIdKey, creator); } } //校验重复>>>>>>>>>>>>>>>>>>> @@ -437,6 +554,8 @@ public static JSONObject parse(@NotNull final RequestMethod method, String name, target.put(EXIST.name(), exist); target.put(UNIQUE.name(), unique); target.put(REMOVE.name(), remove); + target.put(MUST.name(), must); + target.put(REFUSE.name(), refuse); target.put(NECESSARY.name(), necessary); target.put(DISALLOW.name(), disallow); //还原 >>>>>>>>>> @@ -768,10 +887,16 @@ private static void sqlVerify(@NotNull String funChar, @NotNull JSONObject real, return; } - SQLConfig config = creator.createSQLConfig().setMethod(RequestMethod.HEAD).setCount(1).setPage(0); - config.setTable(Test.class.getSimpleName()); + if (rv instanceof String && ((String) rv).contains("'")) { // || key.contains("#") || key.contains("--")) { + throw new IllegalArgumentException(rk + ":value 中value不合法!value 中不允许有单引号 ' !"); + } + + SQLConfig config = creator.createSQLConfig().setMethod(RequestMethod.GET).setCount(1).setPage(0); config.setTest(true); - config.putWhere("'" + rv + "'" + logic.getChar() + funChar, tv, false); + // config.setTable(Test.class.getSimpleName()); + // config.setColumn(rv + logic.getChar() + funChar) + config.putWhere(rv + logic.getChar() + funChar, tv, false); // 字符串可能 SQL 注入,目前的解决方式是加 TYPE 校验类型或者干脆不用 sqlVerify,而是通过远程函数来校验 + config.setCount(1); SQLExecutor executor = creator.createSQLExecutor(); JSONObject result = null; @@ -780,12 +905,12 @@ private static void sqlVerify(@NotNull String funChar, @NotNull JSONObject real, } finally { executor.close(); } - if (result != null && JSONResponse.isExist(result.getIntValue(JSONResponse.KEY_COUNT)) == false) { - throw new IllegalArgumentException(rk + ":" + rv + "中value不合法!必须匹配 " + logic.getChar() + tv + " !"); + if (result != null && JSONResponse.isExist(result.getIntValue(JSONResponse.KEY_CODE)) == false) { + throw new IllegalArgumentException(rk + ":value 中value不合法!必须匹配 '" + tk + "': '" + tv + "' !"); } } - + /**验证是否存在 * @param table * @param key @@ -829,7 +954,7 @@ public static void verifyExist(String table, String key, Object value, long exce public static void verifyRepeat(String table, String key, Object value, @NotNull SQLCreator creator) throws Exception { verifyRepeat(table, key, value, 0, creator); } - + /**验证是否重复 * @param table * @param key @@ -838,6 +963,19 @@ public static void verifyRepeat(String table, String key, Object value, @NotNull * @throws Exception */ public static void verifyRepeat(String table, String key, Object value, long exceptId, @NotNull SQLCreator creator) throws Exception { + verifyRepeat(table, key, value, exceptId, null, creator); + } + + /**验证是否重复 + * @param table + * @param key + * @param value + * @param exceptId 不包含id + * @param idKey + * @param creator + * @throws Exception + */ + public static void verifyRepeat(String table, String key, Object value, long exceptId, String idKey, @NotNull SQLCreator creator) throws Exception { if (key == null || value == null) { Log.e(TAG, "verifyRepeat key == null || value == null >> return;"); return; @@ -845,15 +983,16 @@ public static void verifyRepeat(String table, String key, Object value, long exc if (value instanceof JSON) { throw new UnsupportedDataTypeException(key + ":value 中value的类型不能为JSON!"); } - - + + String finalIdKey = StringUtil.isEmpty(idKey, false) ? apijson.JSONObject.KEY_ID : idKey; + SQLConfig config = creator.createSQLConfig().setMethod(RequestMethod.HEAD).setCount(1).setPage(0); config.setTable(table); if (exceptId > 0) {//允许修改自己的属性为该属性原来的值 - config.putWhere(JSONRequest.KEY_ID + "!", exceptId, false); + config.putWhere(finalIdKey + "!", exceptId, false); } config.putWhere(key, value, false); - + SQLExecutor executor = creator.createSQLExecutor(); try { JSONObject result = executor.execute(config, false); diff --git a/APIJSONORM/src/main/java/apijson/orm/model/Test.java b/APIJSONORM/src/main/java/apijson/orm/model/Test.java index 838fa6398..5b887e383 100755 --- a/APIJSONORM/src/main/java/apijson/orm/model/Test.java +++ b/APIJSONORM/src/main/java/apijson/orm/model/Test.java @@ -7,9 +7,11 @@ import apijson.MethodAccess; -/**条件测试 +/**条件测试。最早可能 4.5.0 移除。AbstractSQLConfig 已支持 SELECT 2>1 这种简单条件表达式, + * 相当于是 SELECT 后只有 WHERE 条件表达式,其它全都没有,这样就可以去掉仅用来动态执行校验逻辑 Test 表了。 * @author Lemon */ +@Deprecated @MethodAccess(POST = {}, PUT = {}, DELETE = {}) public class Test { }