【问题标题】:Send JSON body in HTTP GET request in java/spring boot在 java/spring boot 中的 HTTP GET 请求中发送 JSON 正文
【发布时间】:2020-04-30 13:47:06
【问题描述】:

我需要在 java/spring boot 中发送一个带有 json 正文的 GET 请求。我知道反对它的建议,但是我必须这样做有几个原因: 1. 我使用的第 3 方 API 只允许 GET 请求,所以 POST 不是一个选项。 2. 我需要在正文中传递一个非常大的参数(大约 8-10k 个字符的逗号分隔列表),因此也不可以将查询参数附加到 url。

我尝试了几种不同的方法:

  1. apache HttpClient 来自这里:Send content body with HTTP GET Request in Java。这直接从 API 本身给出了一些关于错误密钥的错误。

  2. URIComponentsBuilder 来自此处:Spring RestTemplate GET with parameters。这只是将参数添加到 url 上,正如我之前解释的那样,这不是一个选项。

  3. restTemplate.exchange。这似乎是最直接的,但对象不会通过:https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#exchange-java.lang.String-org.springframework.http.HttpMethod-org.springframework.http.HttpEntity-java.lang.Class-java.util.Map-

可能还有一两件事我已经忘记了。

Here is what I'm talking about in Postman。我需要能够传递这里给出的两个参数。如果通过 Postman 运行它可以正常工作,但我无法在 Java/Spring Boot 中解决。

这是来自 restTemplate.exchange 尝试的代码 sn-p:

public String makeMMSICall(String uri, List<String> MMSIBatchList, HashMap<String, String> headersList) {
    ResponseEntity<String> result = null;
    try {
        RestTemplate restTemplate = new RestTemplate();

        HttpHeaders headers = new HttpHeaders();
        for (String key : headersList.keySet()) {
            headers.add(key, headersList.get(key));
        }

        Map<String, String> params = new HashMap<String, String>();
        params.put("mmsi", String.join(",", MMSIBatchList));
        params.put("limit", mmsiBatchSize);

        HttpEntity<?> entity = new HttpEntity<>(headers);
        result = restTemplate.exchange(uri, HttpMethod.GET, entity, String.class, params);

        System.out.println(result.getBody());

    } catch (RestClientException e) {
        LOGGER.error("Exception in makeGetHTTPCall :" + e.getMessage());
        throw e;
    } catch (Exception e) {
        LOGGER.error("Exception in makeGetHTTPCall :" + e.getMessage());
        throw e;
    }
    return result.getBody();
}

感谢您的帮助!

【问题讨论】:

  • 如您所说,不建议发送带有 GET 操作的请求正文。但是当您尝试 restTemplate.exchange 时,日志说了什么?你确定你使用了正确的语法吗?如果你能提供一些日志和代码,那会更有帮助。
  • 我在尝试使用 restTemplate.exchange 时添加了一个代码 sn-p。日志没有显示任何异常,除了人们期望在不带参数的情况下对 API 的默认调用所获得的 API 结果。
  • GET 请求不包含正文。你不能做你想做的事。使用 POST、PUT 或其他接受正文的方法。
  • 正如我在问题正文中所述,由于 API 限制,POST/PUT 不是一个选项。我知道这是可能的,因为 Postman 能够做到这一点,并且还有许多其他问题承认这是可能的,但只要你喜欢这里就建议不要这样做:stackoverflow.com/questions/978061/…。在这一点上我无法避免它。

标签: java spring spring-boot


【解决方案1】:

你可以试试java.net.HttpUrlConnection,它对我有用,但实际上我通常使用 POST

HttpURLConnection connection = null;
BufferedReader reader = null;
String payload = "body";

try {

    URL url = new URL("url endpoint");

    if (url.getProtocol().equalsIgnoreCase("https")) {
        connection = (HttpsURLConnection) url.openConnection();
    } else {
        connection = (HttpURLConnection) url.openConnection();
    }
    //  Set connection properties
    connection.setRequestMethod(method); // get or post
    connection.setReadTimeout(3 * 1000);
    connection.setDoOutput(true);
    connection.setUseCaches(false);        

    if (payload != null) {
        OutputStream os = connection.getOutputStream();

        os.write(payload.getBytes(StandardCharsets.UTF_8));

        os.flush();
        os.close();
    }

    int responseCode = connection.getResponseCode();
}

【讨论】:

  • 如何添加标题?我正在使用的 API 在标头中需要一个不记名令牌。
  • 可以使用以下:connection.setRequestProperty("Authorization", "token");
  • 使用 Java 8 String basicAuth = Base64.getEncoder().encodeToString((username+":"+password).getBytes(StandardCharsets.UTF_8)); httpConn.setRequestProperty ("Authorization", "Basic "+basicAuth);
【解决方案2】:

没有办法通过RestTemplate 实现它,即使使用.exchange 方法也是如此。即使我们在函数参数中传递实体,它也不会发送 GET 调用的请求正文。(通过拦截器日志测试)

您可以使用 Apache 客户端来解决这个问题/请求(无论您想怎么称呼它)。您需要的代码如下所示。

  private static class HttpGetWithBody extends HttpEntityEnclosingRequestBase {

    JSONObject requestBody;

    public HttpGetWithBody(URI uri, JSONObject requestBody) throws UnsupportedEncodingException {
      this.setURI(uri);
      StringEntity stringEntity = new StringEntity(requestBody.toString());
      super.setEntity(stringEntity);
      this.requestBody = requestBody;
    }

    @Override
    public String getMethod() {
      return "GET";
    }
  }


  private JSONObject executeGetRequestWithBody(String host, Object entity) throws ClientProtocolException, IOException {
    CloseableHttpClient httpClient = HttpClients.createDefault();
    try{
      JSONObject requestBody = new JSONObject(entity);
      URL url = new URL(host);
      HttpRequest request = new HttpGetWithBody(url.toURI(), requestBody);
      request.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
      request.addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
      HttpResponse response;
      if(url.getPort() != 0) response = httpClient.execute(new HttpHost(url.getHost(), url.getPort()), request);
      else response = httpClient.execute(new HttpHost(url.getHost()), request);

      if(response.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
        JSONObject res = new JSONObject(EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8));
        httpClient.close();
        return res;
      }
    }catch (Exception e){
      log.error("Error occurred in executeGetRequestWithBody. Error: ", e.getStackTrace());
    }
    httpClient.close();
    return null;
}

如果您检查甚至 Apache 客户端库不支持本机传递正文(通过 HttpGet 方法的代码实现检查),因为 GET 请求的上下文请求正文不是一个好的和明显的做法。

【讨论】:

    【解决方案3】:

    尝试创建一个新的自定义 RequestFactory。 相似 get request with body

    【讨论】:

    • 所以想法是添加一个像 RequestFactory 这样的类,然后调用 RequestFactory.getRestTemplate 而不是 new RestTemplate()?
    • 是的,你也可以看看这个演示github.com/mhshimul/spring-demo
    • 我修改了演示中的代码以适应我的目的,但它不起作用。 API 在没有接收到任何参数时仍然返回默认响应。
    猜你喜欢
    • 2019-08-15
    • 1970-01-01
    • 2021-06-20
    • 1970-01-01
    • 2020-04-25
    • 2020-11-13
    • 1970-01-01
    • 2015-11-17
    • 2019-07-07
    相关资源
    最近更新 更多