{"record":{"id":"8a62bc3880c5222a","repo":"Tencent/APIJSON","slug":"visitorid-long-string","errorCode":null,"errorMessage":"visitorId 只能是 Long 或 String 类型！","messagePattern":"visitorId 只能是 Long 或 String 类型！","errorType":"exception","errorClass":"UnsupportedDataTypeException","httpStatus":400,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java","lineNumber":487,"sourceCode":"\t@Override\n\tpublic void verifyLogin() throws Exception {\n\t\t//未登录没有权限操作\n\t\tif (visitorId == null) {\n\t\t\tthrow new NotLoggedInException(\"未登录或登录过期，请登录后再操作！\");\n\t\t}\n\n\t\tif (visitorId instanceof Number) {\n\t\t\tif (((Number) visitorId).longValue() <= 0) {\n\t\t\t\tthrow new NotLoggedInException(\"未登录或登录过期，请登录后再操作！\");\n\t\t\t}\n\t\t}\n\t\telse if (visitorId instanceof String) {\n\t\t\tif (StringUtil.isEmpty(visitorId, true)) {\n\t\t\t\tthrow new NotLoggedInException(\"未登录或登录过期，请登录后再操作！\");\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tthrow new UnsupportedDataTypeException(\"visitorId 只能是 Long 或 String 类型！\");\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void verifyAdmin() throws Exception {\n\t\tthrow new UnsupportedOperationException(\"不支持 ADMIN 角色！如果要支持就在子类重写这个方法\" +\n                \"来校验 ADMIN 角色，不通过则 throw IllegalAccessException!\");\n\t}\n\n\n\t/**验证是否重复\n\t * FIXME 这个方法实际上没有被使用\n\t * @param table\n\t * @param key\n\t * @param value\n\t * @throws Exception\n\t */","sourceCodeStart":469,"sourceCodeEnd":505,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java#L469-L505","documentation":"The final branch of verifyLogin(): visitorId is neither Number nor String (e.g. Boolean, Map, JSONObject), so UnsupportedDataTypeException declares that visitorId must be Long or String. This is an integration bug — the object attached as the visitor identity has the wrong runtime type, and the verifier refuses to guess.","triggerScenarios":"A custom Parser/Verifier sets visitorId to a JSONObject of user fields, a Map, or a boolean flag instead of the id scalar; the first verifyLogin() call on a role-protected request hits the else branch.","commonSituations":"Subclass stores the whole user object as visitorId for convenience; generic deserialization (e.g. from JWT claims) yields a Map; migration changed visitorId type and one code path still assigns the old type.","solutions":["Assign only the id scalar: visitorId = user.getLong(idKey) (Long) or the username string.","Keep the full user object in a separate field of your verifier/session, not in visitorId.","Add a type check at the point where the visitor is attached: assert visitorId instanceof Number || instanceof String."],"exampleFix":"// before\nverifier.visitorId = session.getAttribute(\"user\"); // JSONObject\n\n// after\nJSONObject user = (JSONObject) session.getAttribute(\"user\");\nverifier.visitorId = user.getLong(\"id\");","handlingStrategy":"type-guard","validationCode":"if (!(visitorId instanceof Number) && !(visitorId instanceof String)) {\n    throw new IllegalStateException(\"visitorId must be Long or String, got \" + visitorId.getClass());\n}","typeGuard":"boolean isSupportedVisitorId(Object v) { return v == null || v instanceof Number || v instanceof String; }","tryCatchPattern":"catch (UnsupportedDataTypeException e) { 500 integration bug (not client error); page the owner; fix the visitor attachment code.","preventionTips":["Attach only the id scalar to visitorId; keep full user objects elsewhere.","Add a startup smoke test that performs one login and asserts the visitorId type."],"tags":["apijson","type-error","integration","authentication"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}