{"record":{"id":"01cee1a3e98b761b","repo":"Tencent/APIJSON","slug":"join-e-getkey-key-app-join","errorCode":null,"errorMessage":"join:'${e.getKey()}' 中 ${key} 不合法 ！@ APP JOIN 只允许 key@:/Table/refKey 这种 = 等价连接！","messagePattern":"join:'(.+?)' 中 (.+?) 不合法 ！@ APP JOIN 只允许 key@:/Table/refKey 这种 = 等价连接！","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractParser.java","lineNumber":1697,"sourceCode":"\t\t\tboolean isAppJoin = \"@\".equals(joinType);\n\n\t\t\tM refObj = JSON.createJSONObject();\n\n\t\t\tString key = index < 0 ? null : path.substring(index + 1); // id@\n\t\t\tif (key != null) {  // 指定某个 key 为 JOIN ON 条件\n\t\t\t\tif (key.indexOf(\"@\") != key.length() - 1) {\n\t\t\t\t\tthrow new IllegalArgumentException(apijson.JSONRequest.KEY_JOIN + \":\" + e.getKey() + \" 中 \" + key + \" 不合法！\"\n\t\t\t\t\t\t\t+ \"必须为 &/Table0,</Table1/key1,@/Table1:alias2/key2,... 或 { '&/Table0':{}, '</Table1/key1':{},... } 这种格式！\"\n\t\t\t\t\t\t\t+ \"且 Table:alias 的 alias 必须满足英文单词变量名格式！\");\n\t\t\t\t}\n\n\t\t\t\tif (tableObj.get(key) instanceof String == false) {\n\t\t\t\t\tthrow new IllegalArgumentException(apijson.JSONRequest.KEY_JOIN + \":\" + e.getKey() + \"' 对应的 \"\n            \t\t\t+ tableKey + \":{ \" + key + \": value } 中 value 类型不合法！必须为同层级引用赋值路径 String！\");\n\t\t\t\t}\n\n\t\t\t\tif (isAppJoin && StringUtil.isName(key.substring(0, key.length() - 1)) == false) {\n\t\t\t\t\tthrow new IllegalArgumentException(apijson.JSONRequest.KEY_JOIN + \":'\" + e.getKey() + \"' 中 \" + key + \" 不合法 ！\" +\n\t\t\t\t\t\t\t\"@ APP JOIN 只允许 key@:/Table/refKey 这种 = 等价连接！\");\n\t\t\t\t}\n\n\t\t\t\trefObj.put(key, getString(tableObj, key));\n\t\t\t}\n\n\n\t\t\tSet<Entry<String, Object>> tableSet = tableObj.entrySet();\n\t\t\t// 取出所有 join 条件\n\t\t\tM requestObj = JSON.createJSONObject(); // (Map<String, Object>) obj.clone();\n\n\t\t\tboolean matchSingle = false;\n\t\t\tfor (Entry<String, Object> tableEntry : tableSet) {\n\t\t\t\tString k = tableEntry.getKey();\n\t\t\t\tObject v = k == null ? null : tableEntry.getValue();\n\t\t\t\tif (v == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}","sourceCodeStart":1679,"sourceCodeEnd":1715,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java#L1679-L1715","documentation":"For APP JOIN ('@' join type) with an explicit ON key in the join path, the key minus its trailing '@' must satisfy StringUtil.isName (an English variable name: letter start, letters/digits/underscore). This IllegalArgumentException fires when the key body is not a valid identifier, because APP JOIN only supports simple '=' equivalence joins.","triggerScenarios":"join paths like '@/Comment/to id@' (space), '@/Comment/to-id@' (hyphen), '@/Comment/to@id@' (embedded '@'), or '@/Comment/@' (empty name).","commonSituations":"Using DB column names with hyphens or non-ASCII characters as join keys; hand-building join path strings via string concatenation that leaves stray characters; copy-paste artifacts (spaces, punctuation) inside the key.","solutions":["Rename the join key inside the joined table object and the join path to a plain identifier: letters/digits/underscore, starting with a letter","If the real column has an exotic name, map it via the column alias/dynamic-column mechanism rather than in the join key"],"exampleFix":"// before\n{ 'join': '@/Comment/to-id@', 'Comment': { 'to-id@': '/User/id' } }\n// after\n{ 'join': '@/Comment/toId@', 'Comment': { 'toId@': '/User/id' } }","handlingStrategy":"validation","validationCode":"const NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;\nfunction checkAppJoinKey(joinPath) {\n  const key = joinPath.substring(joinPath.lastIndexOf('/') + 1);\n  if (key && key.endsWith('@') && !NAME_RE.test(key.slice(0, -1))) {\n    return `APP JOIN ON key '${key}' is not a valid identifier`;\n  }\n  return null;\n}","typeGuard":"const isValidOnName = (k) => /^[A-Za-z_][A-Za-z0-9_]*@$/.test(k);","tryCatchPattern":"Catch IllegalArgumentException containing 'APP JOIN 只允许 key@'; report the offending key from the message and reject the request at the client.","preventionTips":["Restrict join ON keys to [A-Za-z_][A-Za-z0-9_]* before '@'","Validate generated keys if table names come from external metadata"],"tags":["apijson","join","app-join","identifier","validation"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}