Home > Enterprise >  Using RestTemplate GET request throws 400 Bad Request
Using RestTemplate GET request throws 400 Bad Request

Time:03-03

I am getting 400 Bad Request when I send a GET request to Rest Service from JUnit Test case. My request body should be text/plain (Content-Type: text/plain) and the response should be a ResponseEntity of type Employee. I am not getting any error when I call request using Postman, but how I'm not able to make it pass using Spring Web Client methods.

I have an example for a POST REQUEST where almost similar test implementation passes, the difference is that in the request body I pass an entire object of Employee and in the test which fails is a text/plain. ( Last added)

I tried to understand why it doesn't work, but I couldn't figure out. I also tried to change exchange Method with getForObject, getForEntity but it didn't work.

Full URL : http://localhost:8080/employee/find/lastName/

L.E.: IntelliJ hellps me by highlighting .exchange method and tells me that that method will respond with 400.

Controller

@RequestMapping (value = "/find/lastName", headers="Content-Type=text/plain", method = RequestMethod.GET)
    public ResponseEntity<Employee> findEmployeeByLastName(@RequestBody String lastName) {
        return EmployeeService.getEmployeeByLastName(lastName);
    }

Service

public static ResponseEntity<Employee> getEmployeeByLastName(String lastName) {
    return employees
            .stream()
            .filter(employee -> employee.getLastName().equals(lastName))
            .findFirst()
            .map(employee -> new ResponseEntity<>(employee, HttpStatus.OK))
            .orElseThrow(() -> new ExmployeeResourceException("Not Found"));
}

JUnit Test

@Test
public void ShouldReturnEmployeeByUsingLastName() {
    RestTemplate restTemplate = new RestTemplate();

    String employeeLastName = "Doe";
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.TEXT_PLAIN);

    HttpEntity<String> request = new HttpEntity<>(employeeLastName, headers);
    ResponseEntity<Employee> responseEntity =
            restTemplate.exchange(
                    HOSTNAME   ENDPOINT   "find/lastName/",
                    HttpMethod.GET,
                    request,
                    Employee.class
            );
}

Error log

22:12:28.619 [main] DEBUG org.springframework.web.client.RestTemplate - Writing [Doe] as "text/plain" using [org.springframework.http.converter.StringHttpMessageConverter@48075da3]
22:12:28.764 [main] DEBUG org.springframework.web.client.RestTemplate - GET request for "http://localhost:8080/employee/find/lastName/" resulted in 400 (null); invoking error handler
org.springframework.web.client.HttpClientErrorException: 400 null at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:63)
    at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:700)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:653)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:531)
    at com.endava.rest.EmployeeTests.ShouldReturnEmployeeByUsingLastName(EmployeeTests.java:90)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
    at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

Example of test which passes

    @Test
    public void ShouldBeAbleToCreateAnEmployee() {
        RestTemplate restTemplate = new RestTemplate();

        Integer id = 3;
        String firstName = "Ionut";
        String lastName = "Popescu";

        Employee newEmployee = new Employee(id, firstName, lastName);
        HttpEntity<Employee> httpEntity = new HttpEntity<>(newEmployee);
        ResponseEntity<Employee> responseEntity =
                restTemplate.exchange(
                        HOSTNAME   ENDPOINT,
                        HttpMethod.POST,
                        httpEntity,
                        Employee.class
                );
}

CodePudding user response:

My request have a body which is a text (string) that's why I added the Content-Type header.

The RestTemplate client doesn't support entities in GET requests. This is lack of support for body in GETs pretty common in clients, given those kinds of APIs are rare.

More info on RestTemplate specifically:

I assume you are using a RestTemplate with a default ClientHttpRequestFactory. In this case, JDK's HttpURLConnection is the underlying HTTP client. HttpURLConnection does not send a request body for GET requests; I've verified that using Wireshark.

If you really are stuck with RestTemplate, your alternative is to use a different ClientHttpRequestFactory. For instance:

import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import java.net.URI;

class HttpComponentsClientHttpRequestFactoryForGetWithBody extends HttpComponentsClientHttpRequestFactory {
    private static final class HttpGetRequestWithBody extends HttpEntityEnclosingRequestBase {
        public HttpGetRequestWithBody(URI uri) { super.setURI(uri); }
        @Override public String getMethod() { return HttpMethod.GET.name(); }
    }
    @Override
    protected HttpUriRequest createHttpUriRequest(HttpMethod httpMethod, URI uri) {
        if (HttpMethod.GET.equals(httpMethod)) {
            return new HttpGetRequestWithBody(uri);
        }
        return super.createHttpUriRequest(httpMethod, uri);
    }
}

Usage:

restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactoryForGetWithBody());   
  • Related