When users receive data from web applications, they may want to add new or delete existing data. With POST requests, users can add information by sending values they wish to upload. A DELETE request allows users to remove existing data from an application. When users send POST or DELETE requests, they are processed by the @RestController. The controller takes the appropriate actions depending on the method.
In this topic, we will learn how to implement POST and DELETE methods in Spring. We will use the REST Resource Naming Guide throughout this topic; it governs standard naming conventions. Use this site if you want to learn more about common API names.
@PostMapping
Suppose you want to create an application where users can add the names and addresses of the people they know. To add a person to the address book, a user needs to send the data to the server, while the server needs to store it somewhere. To make this possible, implement a @PostMapping in the @RestController.
We advise using a thread-safe object to work with data in a @RestController. The controller can get multiple requests simultaneously, which are processed by different threads. If the object is not thread-safe, multiple requests can lead to data loss and other unexpected errors.
In our example, we want to store mappings from people to addresses, so we use a Map object. We can use a ConcurrentHashMap to implement a thread-safe Map in our application.
Java
@RestController
public class AddressController {
private ConcurrentMap<String, String> addressBook = new ConcurrentHashMap<>();
}Kotlin
@RestController
class AddressController {
private val addressBook = ConcurrentHashMap<String, String>()
}With a ConcurrentHashMap, we can set up a @PostMapping that takes a person's name and address and adds them to the Map. Since the user wants to send data with a POST request, we need to use a @RequestParam.
A @RequestParam is a variable provided by a user through the query parameters. It is used during the handling of POST requests. A @RequestParam can be provided in two ways:
In the query parameters section of a REST request. In Postman, it can be found in the Params section, labeled as Query Params.
In the URL path, in the following format:
localhost:<port>/<api-path>?<param>=<value>&<param>=<value>.
In the examples below, the Spring port is set to 8080, so all POST and DELETE requests are sent to localhost:8080.
When we provide a parameter through the query parameters, we need to set a name and a value. The parameter name should match the name of the @RequestParam, and the value should have the same type as the @RequestParam variable. The following code is an example of how a @RequestParam can be used with a @PostMapping to add data to the address book.
Java
@RestController
public class AddressController {
private ConcurrentMap<String, String> addressBook = new ConcurrentHashMap<>();
@PostMapping("/addresses")
public void postAddress(@RequestParam String name, @RequestParam String address) {
addressBook.put(name, address);
}
}Kotlin
@RestController
class AddressController {
private val addressBook = ConcurrentHashMap<String, String>()
@PostMapping("/addresses")
fun postAddress(@RequestParam name: String, @RequestParam address: String) {
addressBook[name] = address
}
}In this @PostMapping, we expect requests with two @RequestParam variables. The first is the name (with the String type). The second is the address (also of the String type). When users send a POST request to the /addresses path, they provide two parameters in the request body. When the request is processed, the name and address are added to ConcurrentHashMap.
To test whether the POST succeeded, you can implement a GET request that returns a requested address based on the provided name.
Java
@RestController
public class AddressController {
private ConcurrentMap<String, String> addressBook = new ConcurrentHashMap<>();
@PostMapping("/addresses")
public void postAddress(@RequestParam String name, @RequestParam String address) {
addressBook.put(name, address);
}
@GetMapping("/addresses/{name}")
public String getAddress(@PathVariable String name) {
return addressBook.get(name);
}
}Kotlin
@RestController
class AddressController {
private val addressBook = ConcurrentHashMap<String, String>()
@PostMapping("/addresses")
fun postAddress(@RequestParam name: String, @RequestParam address: String) {
addressBook[name] = address
}
@GetMapping("/addresses/{name}")
fun getAddress(@PathVariable name: String): String? {
return addressBook[name]
}
}In the previous POST request, we added the name Bob, which is mapped to 123 Younge Street. If we send a request to /addresses/Bob, we expect to retrieve this address.
What happens when a POST parameter is either missing or invalid? A user will receive a 400 Bad Request, as shown below.
If this error occurs, reexamine the parameters to ensure they are correct.
@DeleteMapping
Apart from adding new data, sometimes users need to delete data too. We may want to delete a name from our address book if it is no longer required. In this situation, we can use a @DeleteMapping to send a request to delete a portion of our data.
Using a @RequestParam, we can pass a parameter to the @DeleteMapping handler. In our example, the parameter is the name of the person we want to remove from the address book. Once the name has been provided, we can delete the value from the Map and return a message to indicate that it has been successfully removed.
Java
@RestController
public class AddressController {
private ConcurrentMap<String, String> addressBook = new ConcurrentHashMap<>();
@DeleteMapping("/addresses")
public String removeAddress(@RequestParam String name) {
addressBook.remove(name);
return name + " removed from address book!";
}
}Kotlin
@RestController
class AddressController {
private val addressBook = ConcurrentHashMap<String, String>()
@DeleteMapping("/addresses")
fun removeAddress(@RequestParam name: String): String {
addressBook.remove(name)
return "$name removed from address book!"
}
}To verify that the mapping has been removed, we can send a GET to return the contents of the addressBook variable. Take a look at the snippet below. It shows the whole controller:
Java
@RestController
public class AddressController {
private ConcurrentMap<String, String> addressBook = new ConcurrentHashMap<>();
@PostMapping("/addresses")
public void postAddress(@RequestParam String name, @RequestParam String address) {
addressBook.put(name, address);
}
@GetMapping("/addresses/{name}")
public String getAddress(@PathVariable String name) {
return addressBook.get(name);
}
@GetMapping("/addresses")
public ConcurrentMap<String, String> getAddressBook() {
return addressBook;
}
@DeleteMapping("/addresses")
public String removeAddress(@RequestParam String name) {
addressBook.remove(name);
return name + " removed from address book!";
}
}Kotlin
@RestController
class AddressController {
val addressBook = ConcurrentHashMap<String, String>()
@PostMapping("/addresses")
fun postAddress(@RequestParam name: String, @RequestParam address: String) {
addressBook[name] = address
}
@GetMapping("/addresses/{name}")
fun getAddress(@PathVariable name: String): String? {
return addressBook[name]
}
@GetMapping("/addresses")
fun getAddressBook(): ConcurrentMap<String, String> {
return addressBook
}
@DeleteMapping("/addresses")
fun removeAddress(@RequestParam name: String): String {
addressBook.remove(name)
return "$name removed from address book!"
}
}Once a @DeleteMapping has been established, we only need to send a DELETE request to the /addresses path with the name we want to remove in the query parameters. To test this, let's first populate our Map with data. We can do so by sending a few POST requests to the web application. Consider the following two POST requests:
localhost:8080/addresses?name=Bob&address=123 Younge Streetlocalhost:8080/addresses?name=Alice&address=200 Rideau Street
These requests will add two entries to the Map. The first is for Bob, living at 123 Younge Street. The second is for Alice, living at 200 Rideau Street. We can verify whether the entries were added with a GET request to /addresses.
Suppose that we want to delete the entry associated with Bob. We must send a DELETE request to the /addresses mapping, passing the name parameter with the value Bob.
Once the data have been removed, we can verify that the request was completed successfully by sending another GET request for the whole Map. As we can see, the value for Bob is removed from the Map:
Conclusion
In this topic, we have discussed how we can add and remove data with POST and DELETE requests. With the @RequestParam annotation, data can be sent through the query parameters rather than the path (as is possible with @PathVariable). When we work with the data stored in a @RestController, it is essential to remember that the application can process multiple requests simultaneously. Therefore, it is necessary to use thread-safe objects. They ensure that no thread-related data errors occur. When you work with @RequestParam, remember that a 400 Bad Request error will occur if POST parameters are missing or incorrect. Review the parameters if you happen to see this error, and make sure that none of them are incorrect or missing. This advice will help you build complex but steady REST APIs that can handle user input.