Send a PATCH request in Java
What is a PATCH Request?
A PATCH request is one of the HTTP methods which is used when we want to perform a partial or a full update of an existing resource. The most similar to a PATCH request is a PUT request but a PUT request doesn't allow a partial update which is the main difference between them.
1. How to send a PATCH request in Java?
1.1. Preparing a request
Let's think about an example and let's say that we have our own website on which people can sell some used cars. Imagine that on that website, we use different HTTP methods which help us to fetch and store data and we also support a PATCH request. Now imagine that we have decided to sell our own car on our own website and we already have published a listing as in the example below but after some time we decided to edit a name attribute to include more details about the car which we want to sell. What if we want to update only the name attribute but don't want to touch other attributes, how can we do it via a PATCH request?
{
"id": "10",
"name": "BMW X5",
"data": {
"price": 50000,
"color": "Blue"
}
}
We can prepare our PATCH request using HttpRequest class that was introduced in Java 11. Using HttpRequest class we should provide our request URL which would contain the unique ID of a resource that we want to update. We should set up a Content-Type header to specify the type of our request body which we are sending and we should do it correctly because we need to make sure that the type is the same as REST API expects it to be or otherwise, our request would fail. We also need to specify what kind of HTTP request we are sending so that the REST API would handle the PATCH request as a PATCH and not as something else. And as a last thing, we should provide our request body which should contain the attributes that we want to update, simply as that.
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.restful-api.dev/objects/10"))
.header("Content-Type", "application/json")
.method("PATCH", HttpRequest.BodyPublishers.ofString("{\"name\": \"BMW X5 M Sport\"}"))
.build();
Just one thing to keep in mind is that the ID used in the example above that is equal to 10 is a reserved ID and is used here only for an example purpose. To properly update a resource using a PATCH request through our REST API, you would need to create your own resource using a POST request.
1.2. Sending a request
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
Once a request is ready, we can use HttpClient class to send our PATCH request. After that, we can print a response code and a response body just to validate that the request was successful.
2. How to send a Java Object as request body?
2.1. Creating an Object
In the previous section, we prepared our request body by creating a String which represents a JSON object since this REST API expects a request body to be of type JSON. Yes, it worked but how can we just send some proper Java object instead? Let's start with creating this Object first.
import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.HashMap;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Car {
private String name;
private HashMap<String, Object> data;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public HashMap<String, Object> getData() {
return data;
}
public void setData(HashMap<String, Object> data) {
this.data = data;
}
}
You can notice a JsonInclude annotation with a non-null parameter. Why do we need it and what does it do?
Since a PATCH request allows a partial update and since the default value of the Java Object's variables is null, we need to make sure that our request body would not contain null values. Otherwise, what would happen is that the PATCH request might override the resource attributes which we didn't want to update and it would set the values for them to null. Imagine that you create a new Car object, you set the new "name" value for it but don't do anything with "data" variable. If you send this Object to a REST API via a PATCH request, since the "data" value is null because null is a default value, the REST API would consider it as an update and would set it to null on its side as well. Obviously, this is not what we want to do because we would lose an existing data in this case.
The JsonInclude annotation will help us to avoid this problem when we would use the ObjectMapper class (Section 2.3) to convert our Java Object to a String which represents a JSON. JsonInclude annotation would make sure that ObjectMapper ignores all of the null values and it would not add them to the request body.
2.2. Jackson library
Both JsonInclude annotation and ObjectMapper class which we are going to use in the next session come from an external library which is called Jackson. This library needs to be added to your project dependencies. So, if you are using Maven, copy-paste the code below to your pom.xml or if you are using Gradle, copy-paste it to your "build.gradle" file.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.1</version>
</dependency>
2.3. Object Mapper
Let's initialize now our custom Java Object and let's pass an updated "name" variable value to it. After that, let's create an instance of ObjectMapper class and let's use the writeValueAsString method from this class. This method would convert our Java Object to a String which represents a JSON and since we used a JsonInclude annotation and specified that we do not want to include any null values, the new JSON String created would contain only the variables which have been set, in our case it is a "name" variable only.
Car car = new Car();
car.setName("BMW X5 M Sport");
ObjectMapper objectMapper = new ObjectMapper();
String requestBody = objectMapper.writeValueAsString(car);
Now our program is much more powerful and it can convert the Java Object to a request body of a type JSON. If we print our Request Body, we would get something like this:
{"name":"BMW X5 M Sport"}
3. Putting everything together
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Main {
public static void main(String[] args) throws Exception {
Car car = new Car();
car.setName("BMW X5 M Sport");
ObjectMapper objectMapper = new ObjectMapper();
String requestBody = objectMapper.writeValueAsString(car);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.restful-api.dev/objects/10"))
.header("Content-Type", "application/json")
.method("PATCH", HttpRequest.BodyPublishers.ofString(requestBody))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
}
}