{"record":{"id":"f586763f69356988","repo":"jeecgboot/JeecgBoot","slug":"http-s-url","errorCode":null,"errorMessage":"原始接口路径必须以 / 开头，或填写完整的 http(s) URL","messagePattern":"原始接口路径必须以 / 开头，或填写完整的 http\\(s\\) URL","errorType":"validation","errorClass":"JeecgBootBizTipException","httpStatus":null,"severity":"warning","filePath":"jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/controller/OpenApiController.java","lineNumber":266,"sourceCode":"    private void validOriginUrl(String originUrl) {\n        if (oConvertUtils.isEmpty(originUrl)) {\n            throw new JeecgBootBizTipException(\"原始接口路径不能为空\");\n        }\n        String decoded;\n        try {\n            decoded = java.net.URLDecoder.decode(originUrl, \"UTF-8\");\n            // 二次解码，防止 %252f 这类双重编码绕过\n            decoded = java.net.URLDecoder.decode(decoded, \"UTF-8\");\n        } catch (Exception e) {\n            throw new JeecgBootBizTipException(\"原始接口路径包含非法字符\");\n        }\n        //update-begin---author:scott ---date:20260429  for：【issues/9590】微服务nginx部署openApi接口访问不到-----------\n        // 微服务部署时，OpenAPI 配置的接口可能位于其他微服务模块（如 erp 7003），允许 originUrl 直接配置完整 http(s) URL\n        String lower = decoded.toLowerCase();\n        boolean isFullHttpUrl = lower.startsWith(\"http://\") || lower.startsWith(\"https://\");\n        if (!isFullHttpUrl) {\n            if (!decoded.startsWith(\"/\")) {\n                throw new JeecgBootBizTipException(\"原始接口路径必须以 / 开头，或填写完整的 http(s) URL\");\n            }\n            if (decoded.startsWith(\"//\") || decoded.startsWith(\"/\\\\\")) {\n                throw new JeecgBootBizTipException(\"原始接口路径不能以 // 或 /\\\\ 开头\");\n            }\n            if (lower.contains(\"://\") || lower.startsWith(\"file:\") || lower.startsWith(\"ftp:\") || lower.startsWith(\"gopher:\")\n                    || lower.startsWith(\"jar:\") || lower.startsWith(\"netdoc:\")) {\n                throw new JeecgBootBizTipException(\"原始接口路径仅支持相对路径或 http(s) 完整URL\");\n            }\n        } else {\n            // 即便是完整URL，也禁止其它危险协议（防止 http://x@file:/... 之类的绕过场景）\n            String afterScheme = lower.substring(lower.indexOf(\"://\") + 3);\n            if (afterScheme.contains(\"file:\") || afterScheme.contains(\"ftp:\") || afterScheme.contains(\"gopher:\")\n                    || afterScheme.contains(\"jar:\") || afterScheme.contains(\"netdoc:\")) {\n                throw new JeecgBootBizTipException(\"原始接口路径不允许嵌套 file/ftp/gopher/jar/netdoc 等协议\");\n            }\n        }\n        if (decoded.contains(\"..\")) {\n            throw new JeecgBootBizTipException(\"原始接口路径不能包含 ..\");","sourceCodeStart":248,"sourceCodeEnd":284,"githubUrl":"https://github.com/jeecgboot/JeecgBoot/blob/96fb33f5ec68516da0b0147da06b2eb0419e063a/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/controller/OpenApiController.java#L248-L284","documentation":"Thrown when the (twice-decoded) originUrl is neither a full http(s) URL nor a path starting with '/'. The validator requires relative paths to begin with '/' so the reverse proxy/router treats them as application-internal, preventing scheme-relative or bare-segment routing.","triggerScenarios":"Configuring an OpenAPI origin_url as 'sys/user/list' (missing leading slash), or 'http:\\\\x' which after decoding no longer matches the http:// prefix check.","commonSituations":"Operators copy an endpoint path from controller code but drop the leading slash; frontend forms that strip a leading '/' during trimming; switching from full URL back to relative path without re-adding '/'.","solutions":["Prefix the origin_url with '/', e.g. '/sys/user/list'.","If the target lives on another microservice, supply the full 'http://' or 'https://' URL instead.","Check the admin form for client-side trimming that removes leading slashes."],"exampleFix":"// before: originUrl = \"jeecgdemo/test/list\"\n// after:  originUrl = \"/jeecgdemo/test/list\"","handlingStrategy":"validation","validationCode":"// Ensure the value is either a full http(s) URL or starts with a single '/'\nprivate static boolean isValidOriginShape(String s) {\n    if (s == null) return false;\n    String l = s.toLowerCase();\n    if (l.startsWith(\"http://\") || l.startsWith(\"https://\")) return true;\n    return s.startsWith(\"/\") && !s.startsWith(\"//\") && !s.startsWith(\"/\\\\\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always prefix relative origin URLs with a single '/'.","Use a full http(s) URL for cross-microservice targets.","Guard the admin form against leading-slash trimming."],"tags":["openapi","url-validation","input-validation"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}