@PutMapping in Spring Boot REST

In this tutorial, you’ll learn how to use the @PutMapping annotation in your RESTful web services app to handle HTTP PUT requests with a JSON or XML payload. This allows you to update user details, for example. Here’s an example CURL command to send an HTTP PUT request with JSON data containing user details:

HTTP PUT Request with CURL command

The HTTP PUT request below will be sent to the following URL:

http://localhost:8080/api/users/2d8d4927572642cebccdf5257880a393

Where “2d8d4927572642cebccdf5257880a393” is the id of the user details which we would like to update.

Below is the JSON payload, which contains the new user details:

{
  "lastName":"Kargopolov",
  "firstName":"Sergey"
}

Complete CURL command

curl -X PUT \
  http://localhost:8080/api/users/2d8d4927572642cebccdf5257880a393 \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/json' \
  -d '{
  "lastName":"Kargopolov",
  "firstName":"Sergey"
}'

@PutMapping Annotation to Handle HTTP PUT Request

Next, we will create a @RestController class that includes a method to handle the HTTP PUT request mentioned previously.

Reading Path Variable

To map one of your methods to an HTTP PUT request, you simply need to annotate it with a @PutMapping annotation. However, since the HTTP PUT request above contains a path parameter variable /2d8d4927572642cebccdf5257880a393, you will need to annotate your method with @PutMapping(“/{userId}”) instead.

To read the value of the userId variable defined in @PutMapping(“/{userId}”), annotate one of the method arguments with @PathVariable, specifying it as “String userId”, as shown in the example below:

updateUser(@PathVariable String userId)

Reading JSON Request Body

To read the JSON request body from an HTTP PUT request, you can annotate another method argument with the @RequestBody annotation. The method argument annotated with @RequestBody should be a class into which the JSON request body will be mapped and converted. Below, I will show you the source code of the “UserDetailsRequestModel” class, which I have annotated with @RequestBody.

@RestController
@RequestMapping("users")
public class UserController {

    @Autowired
    UserService userService;
 
    @PutMapping("/{userId}", 
    consumes={MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}, 
    produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE} )
    public UserRest updateUser(@PathVariable String userId, @RequestBody UserDetailsRequestModel requestUserDetails)    {
        UserRest returnValue = new UserRest();

        UserDto userDto = new UserDto();
        BeanUtils.copyProperties(requestUserDetails, userDto);

        UserDto updatedUserDetails = userService.updateUser(userDto, userId);
        BeanUtils.copyProperties(updatedUserDetails, returnValue);

        return returnValue;
    }

}

The UserDetailsRequestModel class

public class UserDetailsRequestModel {
    private String firstName;
    private String lastName;
    private String email;
    private String password;
    
 public String getFirstName() {
  return firstName;
 }
 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }
 public String getLastName() {
  return lastName;
 }
 public void setLastName(String lastName) {
  this.lastName = lastName;
 }
 public String getEmail() {
  return email;
 }
 public void setEmail(String email) {
  this.email = email;
 }
 public String getPassword() {
  return password;
 }
 public void setPassword(String password) {
  this.password = password;
 }
    
}

Please note that the HTTP PUT request that we used previously is targeting the updateUser method in the UserController class, which expects a JSON request body containing values for the firstName, lastName, email, and password fields (which are all defined in the UserDetailsRequestModel class). However, the JSON request body sent in this request only includes values for the firstName and lastName fields. As a result, the email and password fields in the UserDetailsRequestModel class will be initialized with null values.

PUT vs PATCH

While PUT requests are used to completely replace an existing resource, PATCH requests are used to partially update a resource. This means that a PUT request should include the entire representation of the resource being updated, while a PATCH request only needs to include the changes that are being made.

For example, if you have a user resource with properties for first name, last name, and email address, a PUT request would require all three properties to be included in the request body, even if only one property is being updated. On the other hand, a PATCH request could include just the updated property or properties, leaving the other properties unchanged.

Advantages

There are a few advantages to using PATCH over PUT:

  • PATCH requests can be more efficient since they only require sending the changes that are being made rather than the entire representation of the resource.
  • PATCH requests can be safer since they reduce the risk of accidentally overwriting data that should not be updated.
  • PATCH requests can be more flexible since they allow for partial updates to resources that might be very large or complex.

Disadvantages

However, there are some cases where a PUT request might be more appropriate:

  • If the client has no way of knowing the current state of the resource, a PUT request can be used to create a new resource or completely replace an existing one.
  • If the server is not able to efficiently support partial updates, a PUT request might be a better option.
  • If the resource being updated is relatively small and simple, the advantages of using PATCH might not be significant enough to justify the added complexity.

Ultimately, the choice between PUT and PATCH will depend on the specific needs of your application and the resources you are working with.

Best practices when handling PUT requests

  • Use a consistent response format: It’s a good idea to use a consistent response format for all your endpoints, including PUT requests. This can make it easier for clients to understand and work with your API. You could use a JSON object with fields such as “success” and “message” to indicate whether the request was successful or not.
  • Ensure that updates are idempotent: Idempotent operations are those that can be repeated multiple times without causing unintended effects. When handling PUT requests, you should ensure that the operation is idempotent. This means that if the same request is sent multiple times, it should produce the same result as if it were sent only once.
  • Validate the data being sent in the request: It’s important to validate the data being sent in the PUT request to ensure that it’s valid before processing it. You could use Spring Boot’s validation framework to validate the data against a set of rules.
  • Use proper HTTP status codes: You should use proper HTTP status codes to indicate the outcome of the PUT request. For example, if the resource was updated successfully, you could return a 200 OK status code. If the resource was not found, you could return a 404 Not Found status code.
  • Handle errors properly: You should handle errors properly when processing PUT requests. This means returning meaningful error messages to the client and ensuring that errors are logged properly.

By following these best practices, you can ensure that your PUT requests are handled properly and that your API is easy to work with for clients.

@PutMapping – Video Tutorial

Frequently asked questions

  • Should I use a separate DTO for update requests, or can I reuse the same DTO for both creation and updates?
    It’s generally a good practice to use separate DTOs for creating and updating operations, as they often have different requirements and constraints. For example, some fields might be required during creation but not during updates, or some fields might be immutable after creation.
  • Should I return the updated resource in the response or just a success message?
    It is generally a good practice to return the updated resource in the response, as it allows the client to confirm that the update was successful and to see any changes that were made. However, depending on the requirements of your API, you may also choose to return just a success message indicating that the update was successful.
  • How can I handle concurrency issues when multiple clients are trying to update the same resource using PUT requests?
    To handle concurrency issues when multiple clients are trying to update the same resource using PUT requests, you can use optimistic locking. This involves adding a version field to your data model and incrementing it every time the resource is updated. When a client sends a PUT request, it includes the current version of the resource in the request.

Conclusion

In conclusion, this tutorial has shown you how to handle PUT requests in Spring Boot using the @PutMapping annotation. We’ve covered how to read path variables and JSON request bodies, as well as how to use the BeanUtils library to map request data to DTOs. Additionally, we’ve included a complete CURL command for making PUT requests to our API.

By following best practices, such as using a consistent response format, limiting the number of fields that can be updated, and validating request data, you can ensure that your API is reliable and easy to use for clients. We hope this tutorial has been helpful in getting you started with handling PUT requests in your Spring Boot REST applications. Make sure to take a look at the list of video courses above; perhaps one of them will be helpful to you.


Leave a Reply

Your email address will not be published. Required fields are marked *