Fisher Coder
Fisher Coder

Reputation: 3576

GET request in RestTemplate in JSON

It's been troubling for a couple days already for a seemingly super straightforward question:

I'm making a simple GET request using RestTemplate in application/json, but I keep getting

org.springframework.web.client.HttpClientErrorException: 400 Bad Request
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
    at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:636)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:592)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:552)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:470)

I did research and followed this tutorial, also I looked at solutions from this POST request via RestTemplate in JSON. But none of them helped, here's my code:

RestTemplate restTemplate = new RestTemplate();    
HttpHeaders requestHeaders = new HttpHeaders();    
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
restTemplate.exchange(endpoint, HttpMethod.GET, requestEntity, String.class);

endpoint is http://localhost:8080/api/v1/items?itemIds=" + URLEncoder.encode(itemIds, "UTF-8") which works fine in Postman. itemIds is a comma-separated list like below:

5400028914,5400029138,5400029138,5400029138,5400029138,5400028401,5400028918,5400028076,5400028726

I also tried to use getForObject like below:

String result = restTemplate.getForObject(endpoint, String.class);

which gives me this error:

org.springframework.web.client.HttpClientErrorException: 415 Unsupported Media Type

I'm not sure what I missed or did wrong, but the same endpoint works perfectly on Postman, but the only difference is that I added Content-Type header in Postman app.

This is my Request from Postman:

GET /api/v1/items?itemIds=abc%2cdef%2cghi HTTP/1.1 Host: localhost:8080 Connection: keep-alive Postman-Token: 84790e06-86aa-fa8a-1047-238d6c931a68 Cache-Control: no-cache User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 Content-Type: application/json Accept: */* Accept-Encoding: gzip, deflate, sdch Accept-Language: en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4

So how can I correctly set content-type using RestTemplate if my above code is wrong?

Another deep dive, I've fired up Wireshark to capture the two HTTP requests, here are the screenshots:

The request from Postman: request from Postman

The request from my Java code: request from Java program

I still don't see why the one from my Java program throws 400 while the one from Postman works fine.

Thanks a lot.

Upvotes: 1

Views: 19580

Answers (2)

Fisher Coder
Fisher Coder

Reputation: 3576

OK, eventually, one of my co-workers helped me figure out why, believe it or not, it's this simple:

The endpoint was like this: "http:localhost:8080/api/v1/items?itemIds=" + URLEncoder.encode(itemIds, "UTF-8");

However, it should be "http:localhost:8080/api/v1/items?itemIds=" + itemIds;

itemIds is just a comma-separated list.

After URLEncoder encoding via "UTF-8" schema, this comma-separated list becomes itemIds=5400028914%2C5400029138%2C5400029138%2C5400029138%2C5400029138%2C5400028401%2C5400028918%2C5400028076

from

itemIds=5400028914,5400029138,5400029138,5400029138,5400029138,5400028401,5400028918,5400028076,5400028726

We don't need to URLEncoder to encode the URL when using RestTemplate, anyone could help me deepen my understanding here please?

Thanks!

Upvotes: 1

David Doan
David Doan

Reputation: 129

You don't need set "requestEntity" for a GET method, try your code like this:

public ItemInfo getItemInfo() throws Exception {
    String url =
            "http://localhost:8080/api/v1/items?itemIds=abc";
    ObjectMapper objectMapper = new ObjectMapper();     
    ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
    String responseBody = response.getBody();
    handlerError(response, url);
    try {
        return objectMapper.readValue(responseBody, ItemInfo.class);
    } catch (IOException exception) {
        LOGGER.error("failed to send REST request", exception);
        throw new Exception(ErrorCode.NOT_AVAILABLE, url);
    }
}

private void handlerError(final ResponseEntity<String> response, final String url) throws Exception {
    String responseBody = response.getBody();
    try {
        if (RestUtil.isError(response.getStatusCode())) {
            ObjectMapper objectMapper = new ObjectMapper(); 
            MyInfoError myInfoError = objectMapper.readValue(responseBody, MyInfoError.class);
            throw new Exception(infoErreur, ErrorCode.UNKNOWN_CODE_ERROR);
        } else if (RestUtil.isNotFound(response.getStatusCode())) {
            throw new Exception(ErrorCode.NOT_AVAILABLE, "MyService");
        }
    } catch (IOException exception) {
        LOGGER.error("failed to send REST request", exception);
        throw new Exception(ErrorCode.NOT_AVAILABLE, url);
    }
}

I put it NULL because GET method not send any JSON request body/headers:

restTemplate.exchange(url, HttpMethod.GET, null, String.class);

OR: put your headers in GET method like this:

RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<?> requestEntity = new HttpEntity<>(headers);
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);

For Example for POST you need to set requestEntity like below:

ItemFormRequest request =
            new ItemFormRequest(1,"item no", "item name");
HttpEntity<ItemFormRequest> requestEntity = new HttpEntity<>(request);
        ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
        String responseBody = response.getBody();

Hope this help :)

Upvotes: 0

Related Questions