resttemplate.exchange 传递的参数未在服务端自动解码

the passed param by resttemplate.exchange was not decoded in service side automatically

下面是我的 REST API 代码:

 @RequestMapping(value = "/get", method = RequestMethod.POST, produces = { "application/json" })
      @ApiOperation(value = "get data by key.", notes = "return json string value.")
      public JsonObjectResponse<String> get(
          @ApiParam(required = true, name = "regionName", value = "region name") @RequestParam("regionName") String regionName,
          @ApiParam(required = true, name = "key", value = "region key,Default is uuid") @RequestParam("key") String key) throws UnsupportedEncodingException
      {
        JsonObjectResponse<String> jr = new JsonObjectResponse<String>();
        //key = decodeJsonString(key);  // added for junit
        String val = adfService.onPath(regionName).get(key);
        jr.setState(StateCode.SUCCESS);
        jr.setData(JsonObject.create().append(key,val).toJson());

        return jr;
      }

我正在尝试传递参数:

regionName=/fusion/table1&key={"fusionTbl1DetailNo":"fusionNo001","pk":"PK0001"}
  1. 如果我通过swagger-ui调用它,它会这样调用:

    http://localhost:8080/service/basic/get?regionName=%2Ffusion%2Ftable1&key=%7B%22fusionTbl1DetailNo%22%3A%22fusionNo001%22%2C%22pk%22%3A%22PK0001%22%7D&token=8652493a-4147-43f4-af3a-bcb117fb7d42`
    

    它对参数进行了编码,这些参数也可以在服务器端自动正确解码。

  2. 当我想为这个API添加测试用例时,我使用restTemplate.exchange方法,代码如下:

    UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
    for (Entry<String, String> entry : queryParamMap.entrySet()) {
        builder.queryParam(entry.getKey(), entry.getValue());
    }
    if (uriParamMap != null) {
    
        url = builder.buildAndExpand(uriParamMap).toUriString();
    } else {
        url = builder.toUriString();
    }
    if (StringUtils.isEmpty(requestBody)) {
        if (bodyParamMap != null) {
            requestBody = parseMapToParams(bodyParamMap);
        } else {
            requestBody = "";
        }
    }
    HttpHeaders headers = new HttpHeaders();
    MediaType mediaType = new MediaType("application", "json", Charset.forName("UTF-8"));
    headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
    // headers.add(HttpHeaders.CONTENT_TYPE, "application/json");
    // headers.add("Accept", "application/json");
    // headers.set(HttpHeaders.ACCEPT, "application/json");
    headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
    // headers.add("Accept-Encoding", "gzip, deflate, br");
    headers.set("Accept-Charset", "utf-8");
    headers.set("Accept-Encoding", "gzip");
    headers.add("Accept-Language", "zh-CN,zh;q=0.8,en;q=0.6");
    headers.add("User-Agent",
            "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36");
    
    headers.add(TestBase.TOKEN_HEADER, TestBase.getTokenId());
    HttpEntity<String> request = new HttpEntity<>(body, headers);
    restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
    ResponseEntity<String> response = restTemplate.exchange(url, httpMethod, request, String.class);
    localresponse.set(response);
    System.out.println("response:" + response);
    return response;
    

我使用 UriComponentsBuilder 附加参数,它会将 url 格式化为

http://localhost:8080/service/basic/get?regionName=/fusion/table1&key=%7B%22fusionTbl1DetailNo%22:%22fusionNo001%22,%22pk%22:%22PK0001%22%7D

方法exchange。然而,当服务器端收到调用时,它没有解码参数key,它的值仍然是:

%7B%22fusionTbl1DetailNo%22:%22fusionNo001%22,%22pk%22:%22PK0001%22%7D

这是为什么?我比较了 swagger 调用中的 header 设置,添加了额外的设置,没有效果:(。

尝试如下:

ResponseEntity<String> res = restTemplate.exchange("http://localhost:8080/service/basic/get?regionName={arg1}&key={arg2}", HttpMethod.POST, null, String.class,"/fusion/table1", "{\"fusionTbl1DetailNo\":\"fusionNo001\",\"pk\":\"PK0001\"}");

arg1arg2 将替换为

"/fusion/table1""{\"fusionTbl1DetailNo\":\"fusionNo001\",\"pk\":\"PK0001\"}"

我在 requestEntity 中发送 null,因为 uriVariables 中没有请求正文和请求参数。

关于 RestTemplate 的 Spring 文档如下:

For each HTTP method there are three variants: two accept a URI template string and URI variables (array or map) while a third accepts a URI. Note that for URI templates it is assumed encoding is necessary, e.g. restTemplate.getForObject("http://example.com/hotel list") becomes "http://example.com/hotel%20list". This also means if the URI template or URI variables are already encoded, double encoding will occur,

您似乎正在使用采用 URI 模板字符串的 RestTemplate 交换方法,因此您不应对 url 字符串进行编码。

url 字符串首先在

上编码
builder.toUriString()

然后再次通话。所以问题似乎是客户端双重编码,而不是服务器端缺少解码

而不是 toUriString() 使用 UriComponentsBuilder.fromUriString(url).queryParam("name","John Doe").build().toString();