32

我有一个看起来像这样的简单控制器:-

@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) {
        ...
    }
}

基本上,此页面具有以下功能:-

  • 用户访问主页 ( /groups GET)。
  • 用户创建一个新组 ( /groups POST) 或选择一个特定组 ( /groups/1 GET)。
  • 用户编辑现有组 ( /groups/1 POST)。

我了解这两个 GET 请求映射如何在这里工作。映射 #2 已定义,否则 ( /groups/1 GET) 将导致“未找到映射”异常。

我想在这里理解的是为什么映射#3 同时处理 ( /groups POST) 和 ( /groups/1 POST)?/groups POST因为请求映射匹配 URI ,所以它应该在这里处理 () 是有道理的。为什么 ( /groups/1 POST) 不会导致此处抛出“未找到映射”异常?事实上,几乎任何 URI 以 /groups (例如:)开头的 POST/groups/bla/1 POST都将由映射 #3 处理。

有人可以向我提供清楚的解释吗?非常感谢。

澄清

我明白我可以使用更合适的方法(如 GET、POST、PUT 或 DELETE)......或者我可以创建另一个请求映射来处理/groups/{id} POST.

然而,我真正想知道的是……

.... “为什么映射 #3/groups/1 POST也能处理?”

“最接近的匹配”推理似乎不成立,因为如果我删除映射 #2,那么我会认为映射 #1 将处理/groups/1 GET,但它不会,它会导致“未找到映射”异常。

我只是有点难过。

4

4 回答 4

19

这很复杂,我认为最好阅读代码。

在 Spring 3.0 中,魔术是通过public Method resolveHandlerMethod(HttpServletRequest request)内部类的方法完成ServletHandlerMethodResolverorg.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter

每个请求控制器类都存在此类的一个实例,并且有一个handlerMethods包含所有请求方法列表的字段。

但是让我总结一下我是如何理解它的

  • Spring 首先检查是否至少有一个处理程序方法匹配(这可能包含假阴性)
  • 然后它创建所有真正匹配的处理程序方法的映射
  • 然后它按请求路径对地图进行排序:RequestSpecificMappingInfoComparator
  • 并拿下第一个

排序是这样工作的:第RequestSpecificMappingInfoComparator一个在 的帮助下比较路径AntPathMatcher,如果两个方法根据这个相等,那么其他指标(如参数数量、标题数量等)将被考虑到要求。

于 2012-03-16T14:45:05.620 回答
2

Spring 试图找到最接近的映射。
因此,在您的任何 POST 请求的情况下,为请求类型找到的唯一映射是 Mapping# 3。映射 1 或映射 2 都不匹配您的请求类型,因此被忽略。也许您可以尝试删除 Mapping #3,并看到 Spring 抛出运行时错误,因为它没有找到匹配项!

于 2012-03-16T13:57:20.653 回答
1

我会为 /groups/{id} 添加一个 PUT 映射。我猜 POST 也可以,但从 HTTP 的角度来看并不完全正确。

添加 @RequestMapping("/{id}", POST) 应该覆盖它吗?

于 2012-03-16T14:25:24.467 回答
-3

将 @PathVariable 添加到映射 #2 中的 Long id 参数

于 2012-03-16T14:10:54.870 回答