1. Overview
Spring 4.3. introduced some very cool method-level composed annotations to smooth out the handling @RequestMapping in typical Spring MVC projects.
In this article, we will learn how to use them in an efficient way.
2. New Annotations
Typically, if we want to implement the URL handler using traditional @RequestMapping annotation, it would have been something like this:
@RequestMapping(value = "/get/{id}", method = RequestMethod.GET)
The new approach makes it possible to shorten this simply to:
@GetMapping("/get/{id}")
Spring currently supports five types of inbuilt annotations for handling different types of incoming HTTP request methods which are GET, POST, PUT, DELETE and PATCH. These annotations are:
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
- @PatchMapping
From the naming convention we can see that each annotation is meant to handle respective incoming request method type, i.e. @GetMapping is used to handle GET type of request method, @PostMapping is used to handle POST type of request method, etc.
3. How It Works
All of the above annotations are already internally annotated with @RequestMapping and the respective value in the method element.
For example, if we’ll look at the source code of @GetMapping annotation, we can see that it’s already annotated with RequestMethod.GET in the following way:
@Target({ java.lang.annotation.ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @RequestMapping(method = { RequestMethod.GET }) public @interface GetMapping { // abstract codes }
All the other annotations are created in the same way, i.e. @PostMapping is annotated with RequestMethod.POST, @PutMapping is annotated with RequestMethod.PUT, etc.
The full source code of the annotations is available here.
4. Implementation
Let’s try to use these annotations to build a quick REST application.
Please note that since we would use Maven to build the project and Spring MVC to create our application, we need to add necessary dependencies in the pom.xml:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.3.6.RELEASE</version> </dependency>
The latest version of spring-webmvc is available in the Central Maven Repository.
Now, we need to create the controller to map incoming request URL. Inside this controller, we would use all of these annotations one by one.
4.1. @GetMapping
@GetMapping("/get") public @ResponseBody ResponseEntity<String> get() { return new ResponseEntity<String>("GET Response", HttpStatus.OK); }
@GetMapping("/get/{id}") public @ResponseBody ResponseEntity<String> getById(@PathVariable String id) { return new ResponseEntity<String>("GET Response : " + id, HttpStatus.OK); }
4.2. @PostMapping
@PostMapping("/post") public @ResponseBody ResponseEntity<String> post() { return new ResponseEntity<String>("POST Response", HttpStatus.OK); }
4.3. @PutMapping
@PutMapping("/put") public @ResponseBody ResponseEntity<String> put() { return new ResponseEntity<String>("PUT Response", HttpStatus.OK); }
4.4. @DeleteMapping
@DeleteMapping("/delete") public @ResponseBody ResponseEntity<String> delete() { return new ResponseEntity<String>("DELETE Response", HttpStatus.OK); }
4.5. @PatchMapping
@PatchMapping("/patch") public @ResponseBody ResponseEntity<String> patch() { return new ResponseEntity<String>("PATCH Response", HttpStatus.OK); }
Points to note:
- We have used the necessary annotations to handle proper incoming HTTP methods with URI. For example, @GetMapping to handle “/get” URI, @PostMapping to handle “/post” URI and so on
- Since we are making an REST-based application, we are returning a constant string (unique to each request type) with 200 response code to simplify the application. We have used Spring’s @ResponseBody annotation in this case.
- If we had to handle any URL path variable, we can simply do it in much less way we used to do in case of using @RequestMapping.
5. Testing the Application
To test the application we need to create a couple of test cases using JUnit. We would use SpringJUnit4ClassRunner to initiate the test class. We would create five different test cases to test each annotation and every handler we declared in the controller.
Let’s simple the example test case of @GetMapping:
@Test public void giventUrl_whenGetRequest_thenFindGetResponse() throws Exception { MockHttpServletRequestBuilder builder = MockMvcRequestBuilders .get("/get"); ResultMatcher contentMatcher = MockMvcResultMatchers.content() .string("GET Response"); this.mockMvc.perform(builder).andExpect(contentMatcher) .andExpect(MockMvcResultMatchers.status().isOk()); }
As we can see, we are expecting a constant string “GET Response“, once we hit the GET URL “/get”.
Now, let’s create the test case to test @PostMapping:
@Test public void givenUrl_whenPostRequest_thenFindPostResponse() throws Exception { MockHttpServletRequestBuilder builder = MockMvcRequestBuilders .post("/post"); ResultMatcher contentMatcher = MockMvcResultMatchers.content() .string("POST Response"); this.mockMvc.perform(builder).andExpect(contentMatcher) .andExpect(MockMvcResultMatchers.status().isOk()); }
In the same way, we created the rest of the test cases to test all of the HTTP methods.
Alternatively, we can always use any common REST client, for example, PostMan, RESTClient etc, to test our application. In that case, we need to be a little careful to choose correct HTTP method type while using the rest client. Otherwise, it would throw 405 error status.
6. Conclusion
In this article, we had a quick introduction to the different types of @RequestMapping shortcuts for quick web development using traditional Spring MVC framework. We can utilize these quick shortcuts to create a clean code base.
Like always, you can find the source code for this tutorial in the Github project.