validateToken接口测试异常:ID变为%7Bid%7D致查询失败排查
核心问题
你的代码在服务层构建远程请求时,错误地处理了路径变量{id},导致远程接口收到的不是你传入的c8r1p15dv5lr0on,而是URL编码后的%7Bid%7D(也就是{id}的编码结果),这才会触发"Token '%7Bid%7D' not found"的错误,最终返回false。
为什么会出现%7Bid%7D?
%7B是字符{的URL编码,%7D是字符}的URL编码。你的Service层定义了带路径变量的URI:"/SSOServiceToken/validateToken/{id}",但在executeFilteredRequest方法中,处理GET请求时,你没有替换这个{id}占位符,反而把id作为查询参数附加到URL后面。最终构建出的URL是:
http://localhost:8180/SSOServiceToken/validateToken/{id}?id=c8r1p15dv5lr0on
当这个URL被发送到远程服务时,{id}会被自动URL编码成%7Bid%7D,远程服务读取路径中的这个值,自然找不到对应的Token,返回false。
至于Postman测试正常,大概率是你直接调用了远程服务的正确路径(比如/SSOServiceToken/validateToken/c8r1p15dv5lr0on),绕过了服务层的错误逻辑。
修复方案
你需要修改executeFilteredRequest方法,先替换URI中的路径变量,再处理查询参数(如果有的话)。这里提供两种优化思路:
方案1:区分路径变量与查询参数(推荐)
重构方法,将路径变量和查询参数分开处理,逻辑更清晰:
private <T> T executeFilteredRequest(Map<String, Object> pathVariables, Map<String, Object> queryParameters, String uri, Class<T> type, HttpMethod httpMethod) throws JsonProcessingException { RestTemplate restTemplate = restTemplateBuilder.build(); HttpHeaders headers = new HttpHeaders(); headers.set("Accept", MediaType.APPLICATION_JSON_VALUE); headers.setContentType(MediaType.APPLICATION_JSON); // 第一步:替换URI中的路径变量 String expandedUri = UriComponentsBuilder.fromUriString(uri) .buildAndExpand(pathVariables) .toUriString(); // 第二步:构建完整URL并添加查询参数(如果有) UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("http://localhost:8180" + expandedUri); if (queryParameters != null && !queryParameters.isEmpty()) { queryParameters.forEach(builder::queryParam); } // 处理POST请求体 String jsonBody = ""; if (HttpMethod.POST.equals(httpMethod)) { ObjectMapper objectMapper = new ObjectMapper(); jsonBody = objectMapper.writeValueAsString(pathVariables); // 根据实际需求调整请求体内容 } HttpEntity<?> entity = new HttpEntity<>(jsonBody, headers); ResponseEntity<T> response = restTemplate.exchange(builder.toUriString(), httpMethod, entity, type); return response.getBody(); }
然后修改validateToken方法的调用:
@Override public boolean validateToken(String id) throws JsonProcessingException { Map<String, Object> pathVariables = new HashMap<>(); pathVariables.put("id", id); String uri = "/SSOServiceToken/validateToken/{id}"; HttpMethod httpMethod = HttpMethod.GET; // 这里没有查询参数,传入null即可 return executeFilteredRequest(pathVariables, null, uri, Boolean.class, httpMethod); }
方案2:快速修复现有逻辑
如果不想大改方法,也可以直接在现有方法中先替换路径变量:
private <T> T executeFilteredRequest(Map<String,Object> parameters, String uri, Class<T> type, HttpMethod httpMethod) throws JsonProcessingException { RestTemplate restTemplate = restTemplateBuilder.build(); HttpHeaders headers = new HttpHeaders(); headers.set("Accept", MediaType.APPLICATION_JSON_VALUE); headers.setContentType(MediaType.APPLICATION_JSON); // 先替换路径变量 String expandedUri = UriComponentsBuilder.fromUriString(uri) .buildAndExpand(parameters) .toUriString(); UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("http://localhost:8180" + expandedUri); String jsonBody=""; if (httpMethod == HttpMethod.POST){ ObjectMapper objectMapper=new ObjectMapper(); jsonBody=objectMapper.writeValueAsString(parameters); } // 注意:如果parameters里有查询参数,这里需要单独处理,避免重复添加 HttpEntity<?> entity = new HttpEntity<>(jsonBody,headers); ResponseEntity<T> response = restTemplate.exchange(builder.toUriString(), httpMethod, entity, type); return response.getBody(); }
验证修复
修改后,你的测试用例调用Controller时,服务层会正确生成URL:
http://localhost:8180/SSOServiceToken/validateToken/c8r1p15dv5lr0on
远程服务能找到对应的Token,返回true,Controller也就会返回200 OK和true的结果,符合你的预期。
内容的提问来源于stack exchange,提问作者Nivel23

