Course – LS – All
announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

When updating objects via REST APIs, it’s a good practice to use the PATCH method. This allows us to perform a partial update using the fields we wish to change. We could also use the PUT method when the existing resource needs to be entirely changed.

In this tutorial, we’ll learn how to set up the HTTP PATCH method in OpenFeign. We’ll also see an unexpected error while testing the PATCH method in Feign client.

Finally, we’ll understand the root cause and fix the problem.

2. Example Application in Spring Boot

Let’s imagine we need to build a simple microservice that calls a downstream service to do partial updates.

2.1. Maven Dependencies

First, we’ll add the spring-boot-starter-web and spring-cloud-starter-openfeign dependencies:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2.2. Implement the Feign Client

Now, let’s implement the PATCH method in Feign using the Spring web annotation.

First, let’s model the User class with a few properties:

public class User {
    private String userId;
    private String userName;
    private String email;
}

Next, we’ll implement the UserClient interface with the updateUser method:

@FeignClient(name = "user-client", url = "http://localhost:8082/api/user")
public interface UserClient {
    @RequestMapping(value = "{userId}", method = RequestMethod.PATCH)
    User updateUser(@PathVariable(value = "userId") String userId, @RequestBody User user);
}

In the above PATCH method, we pass the User object with only the required fields to update along with the userId field. It makes it less cumbersome than sending the whole resource representation, saves some network bandwidth, and avoids contention when multiple updates come for the same object across different fields.

In contrast, had we used the PUT request, we’d have to pass the complete resource representation to replace an existing resource.

3. Implement Test for the Feign Client

Let’s now implement a test case for the UserClient by mocking the HTTP call.

3.1. Setup WireMock Server

To experiment, we’ll need to use a mocking framework to simulate the service we’re calling.

First, let’s include the WireMockServer Maven dependency:

<dependency>
    <groupId>com.github.tomakehurst</groupId>
    <artifactId>wiremock-jre8</artifactId>
    <version>2.35.0</version>
    <scope>test</scope>
</dependency>

Then, let’s configure and start the WireMockServer:

WireMockServer wireMockServer = new WireMockServer(8082);
configureFor("localhost", 8082);
wireMockServer.start();

The WireMockServer is started at the same host and port that the Feign client is configured to use.

3.2. Stubbing the PATCH API

We’ll mock the PATCH method to test the update User API:

String updatedUserResponse = "{\n" +
    "\"userId\": 100001,\n" +
    "\"userName\": \"name\",\n" +
    "\"email\": \"[email protected]\"\n" +
    "}";
stubFor(patch(urlEqualTo("/api/user/".concat(USER_ID)))
  .willReturn(aResponse().withStatus(HttpStatus.OK.value())
  .withHeader("Content-Type", "application/json")
  .withBody(updatedUserResponse)));

3.3. Testing the PATCH Request

To test, we’ll pass the User object to the UserClient with the fields required to update.

Now, let’s complete the test and verify the update functionality:

User user = new User();
user.setUserId("100001");
user.setEmail("[email protected]");
User updatedUser = userClient.updateUser("100001", user);

assertEquals(user.getUserId(), updatedUser.getUserId());
assertEquals(user.getEmail(), updatedUser.getEmail());

It’s expected that the above test should pass. Instead, we’ll get an unexpected error from the Feign client:

feign.RetryableException: Invalid HTTP method: PATCH executing PATCH http://localhost:8082/api/user/100001
        at feign.FeignException.errorExecuting(FeignException.java:268)
	at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:131)
	at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:91)
	at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:100)
	at jdk.proxy2/jdk.proxy2.$Proxy80.updateUser(Unknown Source)
	at com.baeldung.cloud.openfeign.patcherror.client.UserClientUnitTest.givenUserExistsAndIsValid_whenUpdateUserCalled_thenReturnSuccess(UserClientUnitTest.java:64)
	...

Next, let’s investigate the error in detail.

3.4. Cause of the Invalid HTTP Method Error

The above error message indicates that the requested HTTP method is invalid. Though, as per HTTP standards, the PATCH method is valid.

We’ll see from the error message that it’s caused by the ProtocolException class and propagated from the HttpURLConnection class:

Caused by: java.net.ProtocolException: Invalid HTTP method: PATCH
	at java.base/java.net.HttpURLConnection.setRequestMethod(HttpURLConnection.java:489)
	at java.base/sun.net.www.protocol.http.HttpURLConnection.setRequestMethod(HttpURLConnection.java:598)
	at feign.Client$Default.convertAndSend(Client.java:170)
	at feign.Client$Default.execute(Client.java:104)
	at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:119)

It turns out that the default HTTP client uses the HttpURLConnection class to establish the HTTP connection. The HttpURLConnection has a setRequestMethod method to set the request method.

Unfortunately, the HttpURLConnection class does not recognize the PATCH method as a valid type.

4. Fixing the PATCH Method Error

To fix the error, we’ll add a supported HTTP client dependency. Also, we’ll override the default HTTP client by adding a configuration.

4.1. Adding the OkHttpClient Dependency

Let’s include the feign-okhttp dependency:

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
</dependency>

We should note that any other supported HTTP clients, such as ApacheHttpClient, will also work.

4.2. Enable the OkHttpClient

The OkHttpClient class considers the PATCH method as a valid type and does not throw any exceptions.

Let’s enable the OkHttpClient class using the configuration below:

feign.okhttp.enabled=true

Finally, we’ll re-run the test and validate if the PATCH method works. Now, we don’t get any error from the Feign client:

UserClientUnitTest.givenUserExistsAndIsValid_whenUpdateUserCalled_thenReturnSuccess: 1 total, 1 passed

5. Conclusion

In this article, we’ve learned how to use the PATCH method in OpenFeign. We also saw an unexpected error while testing and understood the root cause.

We’ve also fixed the problem using the OkHttpClient implementation.

As always, the example code can be found over on GitHub.

Course – LS – All
announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

res – Microservices (eBook) (cat=Cloud/Spring Cloud)