Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding How Spring MVC's @RequestMapping POST Works

I have a simple Controller that looks like this:-

@Controller @RequestMapping(value = "/groups") public class GroupsController {     // mapping #1     @RequestMapping(method = RequestMethod.GET)     public String main(@ModelAttribute GroupForm groupForm, Model model) {         ...     }      // mapping #2     @RequestMapping(value = "/{id}", method = RequestMethod.GET)     public String changeGroup(@PathVariable Long id, @ModelAttribute GroupForm groupForm, Model model) {         ...     }      // mapping #3     @RequestMapping(method = RequestMethod.POST)     public String save(@Valid @ModelAttribute GroupForm groupForm, BindingResult bindingResult, Model model) {         ...     } } 

Basically, this page has the following functionalities:-

  • User visits main page (/groups GET).
  • User creates a new group (/groups POST) or selects a specific group (/groups/1 GET).
  • User edits an existing group (/groups/1 POST).

I understand how both GET request mappings work here. Mapping #2 is defined, otherwise (/groups/1 GET) will cause a "No mapping found" exception.

What I'm trying to understand here is why mapping #3 handles both (/groups POST) and (/groups/1 POST)? It makes sense that it should handle (/groups POST) here since the request mapping matches the URI. Why (/groups/1 POST) isn't causing a "No mapping found" exception being thrown here? In fact, it almost seems like any POST with URI beginning with /groups (ex: /groups/bla/1 POST) will also be handled by mapping #3.

Can someone provide a clear explanation of this to me? Thanks much.

CLARIFICATION

I understand the fact that I can use more appropriate methods (like GET, POST, PUT or DELETE)... or I can create yet another request mapping to handle /groups/{id} POST.

However, what I want to really know is...

.... "Why does mapping #3 handle /groups/1 POST too?"

The "closest match" reasoning don't seem to hold true because if I remove mapping #2, then I would think mapping #1 will handle /groups/1 GET, but it doesn't and it causes a "No mapping found" exception.

I'm just a little stumped here.

like image 737
limc Avatar asked Mar 16 '12 13:03

limc


People also ask

What does @RequestMapping do in Spring?

One of the most important annotations in spring is the @RequestMapping Annotation which is used to map HTTP requests to handler methods of MVC and REST controllers. In Spring MVC applications, the DispatcherServlet (Front Controller) is responsible for routing incoming HTTP requests to handler methods of controllers.

What is true about @RequestMapping annotation in Spring MVC?

The @RequestMapping annotation can be applied to class-level and/or method-level in a controller. The class-level annotation maps a specific request path or pattern onto a controller. You can then apply additional method-level annotations to make mappings more specific to handler methods.

How can @GetMapping annotation be written by using @RequestMapping method?

Specifically, @GetMapping is a composed annotation that acts as a shortcut for @RequestMapping(method = RequestMethod. GET) . @GetMapping supports the consumes attribute like @RequestMapping .


1 Answers

This is complicated, I think it is better to read the code.

In Spring 3.0 The magic is done by method public Method resolveHandlerMethod(HttpServletRequest request) of the inner class ServletHandlerMethodResolver of org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.

An instance of this class exists for every Request Controller Class, and has a field handlerMethods that contains a list of all the request methods.

But let me summarize how I understand it

  • Spring first checks if at least one handler method matches (this can contain false negatives)
  • Then it creates a map of all really matching handler methods
  • Then it sorts the map by request path: RequestSpecificMappingInfoComparator
  • and takes the first one

The sorting works this way: the RequestSpecificMappingInfoComparator first compares the path with the help of an AntPathMatcher, if two methods are equal according to this, then other metrics (like number of parameters, number of headers, etc.) are taken into account with respect to the request.

like image 162
Ralph Avatar answered Sep 27 '22 21:09

Ralph