{"record":{"id":"45b3fac837787fd4","repo":"dromara/Sa-Token","slug":"javaweb","errorCode":null,"errorMessage":"当前线程非JavaWeb环境","messagePattern":"当前线程非JavaWeb环境","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-client/src/main/java/com/pj/utils/SoMap.java","lineNumber":578,"sourceCode":"//\t\ttry {\n//\t\t\treturn JSON.toJSONString(this, true); \n//\t\t} catch (Exception e) {\n//\t\t\tthrow new RuntimeException(e);\n//\t\t}\n//\t}\n\n\t// ============================= web辅助 =============================\n\n\n\t/**\n\t * 返回当前request请求的的所有参数 \n\t * @return\n\t */\n\tpublic static SoMap getRequestSoMap() {\n\t\t// 大善人SpringMVC提供的封装 \n\t\tServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();\n\t\tif(servletRequestAttributes == null) {\n\t\t\tthrow new RuntimeException(\"当前线程非JavaWeb环境\");\n\t\t}\n\t\t// 当前request\n\t\tHttpServletRequest request = servletRequestAttributes.getRequest(); \n\t\tif (request.getAttribute(\"currentSoMap\") == null || request.getAttribute(\"currentSoMap\") instanceof SoMap == false ) {\n\t\t\tinitRequestSoMap(request);\n\t\t}\n\t\treturn (SoMap)request.getAttribute(\"currentSoMap\");\n\t}\n\n\t/** 初始化当前request的 SoMap */\n\tprivate static void initRequestSoMap(HttpServletRequest request) {\n\t\tSoMap soMap = new SoMap();\n\t\tMap<String, String[]> parameterMap = request.getParameterMap();\t// 获取所有参数 \n\t\tfor (String key : parameterMap.keySet()) {\n\t\t\ttry {\n\t\t\t\tString[] values = parameterMap.get(key); // 获得values \n\t\t\t\tif(values.length == 1) {\n\t\t\t\t\tsoMap.set(key, values[0]);","sourceCodeStart":560,"sourceCodeEnd":596,"githubUrl":"https://github.com/dromara/Sa-Token/blob/ac2c7f6e94a78573cf0bcb932dd8b04e68fad189/sa-token-demo/sa-token-demo-oauth2/sa-token-demo-oauth2-client/src/main/java/com/pj/utils/SoMap.java#L560-L596","documentation":"Thrown by SoMap.getRequestSoMap() when Spring's RequestContextHolder.getRequestAttributes() returns null on the current thread — meaning the call is not executing inside an HTTP request bound by Spring MVC. The static helper relies on SpringMVC's thread-local request context, which only exists on request-handler threads.","triggerScenarios":"Calling getRequestSoMap() (or any code path that uses it) from: a @Scheduled method, an @Async method without context propagation, a newly spawned thread / CompletableFuture lambda, a Filter running before RequestContextFilter is registered, or a unit test that never set up MockHttpServletRequest via RequestContextHolder.setRequestAttributes(...).","commonSituations":"Refactoring a controller helper into a background job; running demos under Servlet 3 async dispatch where the context is not propagated; plain junit tests calling controller logic directly.","solutions":["Move the getRequestSoMap() call onto the HTTP request thread (inside the controller) and pass the SoMap/values as arguments to the async code.","For tests: RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(new MockHttpServletRequest()));","If truly needed on other threads, capture RequestContextHolder.setRequestAttributes(attributes, true) (inheritable) before spawning — but explicit parameter passing is safer.","Check that spring-boot-starter-web (not just webflux) is on the classpath so ServletRequestAttributes is what gets bound."],"exampleFix":"// before\n@Async\npublic void process() {\n    SoMap soMap = SoMap.getRequestSoMap(); // throws\n}\n\n// after\npublic void handle(HttpServletRequest request) {\n    SoMap soMap = SoMap.getRequestSoMap();\n    process(soMap.getString(\"code\"));\n}\n\n@Async\npublic void process(String code) { ... }","handlingStrategy":"validation","validationCode":"ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();\nif (attrs == null) {\n    // not on a request thread: pass values explicitly instead of calling getRequestSoMap()\n}","typeGuard":"boolean isRequestThread() { return RequestContextHolder.getRequestAttributes() != null; }","tryCatchPattern":null,"preventionTips":["Never call getRequestSoMap from @Async/@Scheduled/new threads; capture the SoMap on the handler thread and pass it down.","In tests, set MockHttpServletRequest via RequestContextHolder.setRequestAttributes before invoking controller code."],"tags":["spring-mvc","thread-context","requestcontextholder","async"],"backgroundTag":null,"analyzedSha":"ac2c7f6e94a78573cf0bcb932dd8b04e68fad189","analyzedAt":"2026-08-14T14:36:10.271Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}