As you know, the GET HTTP method is by no means the only available option. This lesson covers how to POST information to a server through RestTemplate. Usage is very similar to sending GET requests, but slight differences exist.
Example Location:com.codingnomads.springweb.resttemplate.POST
RestTemplate POST Methods
Three methods are available for submitting POST requests using RestTemplate. Each is intended to create a new resource by POSTing the given object to the request URL, but the return behavior is different for each:
postForObject(): Returns a POJO directly mapped from the data in the response body. Remember that for POST requests, most servers will return a copy of the data you sent. Sometimes the information has been edited a bit, sometimes it's a carbon copy.postForEntity(): Return a ResponseEntity instance with an embedded POJO mapped from the response body.postForLocation(): Returns the URI of the newly created resource as indicated in theLocationheader of the response.
Tip: No, URI is not a typo above. A URI is used to identify resources on the internet. A URL is a type of URI that identifies a resource and provides means to locate it. In short, all URLs are URIs, but not all URIs are URLs: some are URNs (Uniform Resource Name). You will encounter both URI and URL used frequently moving forward (sometimes interchangeably). It can be confusing, but since URNs are not covered in this course, you can use a URL any time you see URI.
Along with the GET methods described in the previous lesson, the POST methods provided RestTemplate share the same two options for composing the request URL:
- You can manually construct your URL by concatenating path variables and query parameters.
- You can establish placeholders in your URL string, then pass in a
Map<String, String>of parameters and let RestTemplate parse them onto the URL during execution.
Project Setup
These examples use the CodingNomads Demo API.
Let's start with a POJO based on the JSON returned from the API:
@Data
@Builder
public class Task {
private long id;
private long userId;
private String name;
private String description;
private long updatedAt;
private long createdAt;
private boolean completed;
}
Interestingly, the Task class is enough for posting Task data to the API. However, if you want to access the object returned by the server after completion, you need to make sure to match this JSON response:
{
"data": {
"id": 92,
"userId": 3,
"name": "random thing",
"description": "a random description",
"createdAt": 1612285335763,
"updatedAt": 1612285335763,
"completed": true
},
"error": {
"message": "none"
},
"status": "201 CREATED"
}
If you don't match this more complicated structure, the JSON parser won't be able to map the data correctly. To solve this, you'll need to model the appropriate data structure. As you can see in the above JSON, this consists of:
data- this is actually a Task object!error- this is also an object, separate from the Task object.status- a String.
To map this response, you require a broader POJO containing fields for each of the three values. This example will wrap the values into an POJO called ResponseObject. First, you need to create your new Error class.
Info: To find the structure of the Error object returned from the server, you may have to cause an error intentionally.
@Data
public class Error {
String message;
}
Now to create the ResponseObject class:
@Data
public class ResponseObject {
Task data;
Error error;
String status;
}
Now that you have seen the POJOs above, you can make an actual POST request using RestTemplate. Like in the previous lesson, this example uses a CommandLineRunner Bean.
postForObject() Example
The important stuff here is in the run() method. Take your time and see if you can decipher most of it.
@SpringBootApplication
public class postForObjectMain {
@Autowired
RestTemplate restTemplate;
public static void main(String[] args) {
SpringApplication.run(postForObjectMain.class, args);
}
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
@Bean
public CommandLineRunner run() throws Exception {
return args -> {
Task newTask = Task.builder()
.name("learn how to use postForObject()")
.description("get comfortable using the " +
"RestTemplate postForObject() method")
.userId(5)
.completed(false)
.build();
// make POST request using postForObject()
ResponseObject taskReturnedByServerAfterPost =
restTemplate.postForObject(
"http://demo.codingnomads.co:8080/tasks_api/tasks",
newTask,
ResponseObject.class
);
if (taskReturnedByServerAfterPost != null) {
System.out.println(
taskReturnedByServerAfterPost.toString());
}
};
}
}
Now for a detailed explanation:
- The first thing the
run()method does is use the Task builder to instantiate a new Task callednewTask. - Next,
postForObject()is called with the following parameters:- URL: The location to POST new tasks to.
- Request: The
newTaskobject as the body of the request. - Response Type: The response body will get mapped onto an instance of this class - your new ResponseObject class.
- To avoid throwing a
NullPointerException, check if the ResponseObject is null before printing the result withtoString().
Learn by Doing
Package: springweb.resttemplate.POST.postForObject
- Reference the demo API documentation, then create a
UserPOJO. - Now, use RestTemplate to POST a few new users to the demo API.
postForEntity() Example:
Now that you've learned how to use postForObject(), you can move on to the more complicated postForEntity() method. Take a look at this run() method. Keep in mind that other components of the class in which this method exists are identical to the previous example and are therefore omitted.
@Bean
public CommandLineRunner run() throws Exception {
return args -> {
Task newTask = Task.builder()
.name("learn how to use postForEntity()")
.description("get comfortable using the " +
"RestTemplate postForEntity() method")
.userId(5)
.completed(false)
.build();
ResponseEntity<?> responseEntity =
restTemplate.postForEntity(
"http://demo.codingnomads.co:8080/tasks_api/tasks",
newTask,
ResponseObject.class
);
if (responseEntity.getStatusCode().equals(HttpStatus.CREATED)) {
// print body
System.out.println(
Objects.requireNonNull(responseEntity.getBody()));
} else {
// print error if status was not 201 CREATED
System.out.println(
Objects.requireNonNull(
responseEntity.getBody()).getError());
}
};
}
As in the previous lesson, the return type is the main difference between these two methods. In contrast to a POJO, ResponseEntity provides access to response headers, status codes, and more. The rest of the example is essentially the same. Not too bad, eh?
Learn by Doing
Package: springweb.resttemplate.POST.postForEntity
- Create a couple of new Users using
postForEntity(). - Intentionally POST incomplete data to inspect and verify the response and behavior.
postForLocation() Example:
If a server is following the HTTP standards - which is in no way guaranteed - it will supply the URL of a newly created object in a header named Location. The content of this header is what postForLocation() will return to you. This can be helpful when you want to reference the newly created resource immediately.
Tip: Since a ResponseEntity instance also contains the HTTP headers returned from a request, you do not have to use postForLocation() to access the Location header. The next example shows you how to do both.
@Bean
public CommandLineRunner run() throws Exception {
return args -> {
Task newTask = Task.builder()
.name("learn how to use postForLocation()")
.description("get comfortable using the " +
"RestTemplate postForLocation() method")
.userId(5)
.completed(false)
.build();
// use postForLocation() to get the URL for the new resource
URI returnedLocation =
restTemplate.postForLocation(
"http://demo.codingnomads.co:8080/tasks_api/tasks",
newTask,
ResponseObject.class
);
System.out.println(Objects.requireNonNull(returnedLocation));
ResponseEntity<?> responseEntity =
restTemplate.postForEntity(
"http://demo.codingnomads.co:8080/tasks_api/tasks",
newTask,
ResponseObject.class
);
System.out.println(
responseEntity.getHeaders().get("Location"));
};
}
Will produce output similar to this:
/tasks/35
[/tasks/36]
Learn by Doing
Package: springweb.resttemplate.POST.postForLocation
- POST a few Users using
postForLocation(), then immediately use the returned locations to GET the new resources. - Spend some time in the debugger and view what other headers are present in the ResponseEntity.
Nicely done! Please push your work to GitHub when you're done.
Summary: Post Requests with RestTemplate
The essential takeaways from this lesson are that there are three ways to POST using RestTemplate:
postForObject(): maps the response body directly to a POJO.postForEntity(): returns a ResponseEntity<?>.postForLocation(): returns a URL indicating the location of the newly created resource.
Each of the three methods listed here accepts:
- A URL to indicate where the request should be sent.
- The object you want to POST to the URL.
- An optional
Mapto pass query parameters.