【问题标题】:Understanding How Spring MVC's @RequestMapping POST Works了解 Spring MVC 的 @RequestMapping POST 是如何工作的
【发布时间】:2012-03-16 13:49:26
【问题描述】:

我有一个简单的控制器,如下所示:-

@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,但它不会,它会导致“未找到映射” " 例外。

我只是有点难过。

【问题讨论】:

  • 为什么不使用 PUT 来更新资源?那将是正确的 HTTP 协议。
  • web表单提交只支持GET和POST,我这里不做AJAX调用,所以我现在不能依赖PUT和DELETE。
  • @limc,这不是真的,可以在 org.springframework.web.filter.HiddenHttpMethodFilter 的帮助下(在服务器端)将 POST 修改为其他请求类型
  • @Ralph,我的错...我刚才正在阅读,我意识到我可以使用_method hack。根据您的帖子,我现在正在浏览spring源代码。

标签: java spring spring-mvc request-mapping


【解决方案1】:

这个比较复杂,我觉得还是看代码比较好。

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

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

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

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

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

【讨论】:

  • 哇...我浏览了resolveHandlerMethod(...),谈论超高圈复杂度的代码,我在第n个嵌套if语句后迷路了。我在RequestSpecificMappingInfoComparator 上阅读了关于订单列表的javadoc。我很好奇为什么它们对于 GET 和 POST 方法的行为不同。换句话说,如果我删除映射 #2,为什么映射 #1 不处理 /groups/1 GET 而是 Spring 抛出异常...
  • @Ralph - 很好的内部解释
【解决方案2】:

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

【讨论】:

  • 我最初也认为 Spring 正在寻找最接近的匹配项。但是,我意识到这并不完全正确,因为如果是这种情况,我应该能够删除映射#2,并且/groups/1 GET 应该由映射#1 处理,因为它是最接近的匹配......但我'我在这里得到一个“未找到映射”异常。我找不到任何 Spring 文档来解释这种情况。
【解决方案3】:

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

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

【讨论】:

  • 如何在不使用 AJAX 调用的情况下使用 PUT 提交 Web 表单?我仍然很想知道为什么 Spring 在我目前的情况下会这样。
  • 也许还可以查看如何使用 spring 模拟 PUT。stackoverflow.com/questions/4362791/…
  • 您没有为组/{id} 映射定义处理程序。我也会认为这是一个错误,因为我很难看到你所看到的行为的用例。
【解决方案4】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-02
    • 1970-01-01
    • 2013-12-22
    • 1970-01-01
    • 1970-01-01
    • 2015-06-22
    相关资源
    最近更新 更多