2012-03-16 63 views
32

我有一個簡單的控制器,看起來像這樣: -瞭解Spring MVC的@RequestMapping POST如何運作

@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)?由於請求映射與URI匹配,所以它應該在這裏處理(/groups POST)是有意義的。爲什麼(/groups/1 POST)不會在此處引發「找不到映射」異常?事實上,它幾乎看起來像URI以/組開頭的任何POST(例如:/groups/bla/1 POST)也將通過映射#3進行處理。

有人可以向我提供一個清晰的解釋嗎?非常感謝。

澄清

我明白一個事實,我可以用更適當的方法(如GET,POST,PUT或DELETE)......或者我可以創建另一個請求映射到處理/groups/{id} POST

不過,我想知道的就是......

.... 「爲什麼映射#3手柄/groups/1 POST嗎?」

「最接近的匹配」的理由似乎並不成立的,因爲如果我刪除映射#2,那麼我想映射#1將處理/groups/1 GET,但它沒有做,它會導致一個「找不到映射「例外。

我只是有點難倒在這裏。

+0

爲什麼不使用PUT對資源的更新?這將是正確的HTTP協議。 – 2012-03-16 14:23:44

+0

Web表單提交只支持GET和POST,而我沒有在這裏進行AJAX調用,因此我不能依賴於PUT和DELETE。 – limc 2012-03-16 14:25:56

+0

@limc,這是不是真的,柱可以修改(服務器端)到其他請求類型與'org.springframework.web.filter.HiddenHttpMethodFilter' – Ralph 2012-03-16 14:53:30

回答

19

這很複雜,我認爲最好是閱讀代碼。

在春天3.0魔術是通過內部類ServletHandlerMethodResolver的方法public Method resolveHandlerMethod(HttpServletRequest request)org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter

這個類的一個實例存在爲每個請求控制器類,以及具有包含的所有請求方法列表的領域handlerMethods

但讓我總結一下我是如何理解它

  • Spring首先檢查是否至少有一個處理方法一致(這可以包含假陰性)
  • 然後創建地圖上所有真正匹配的處理方法
  • 然後它按請求路徑地圖:RequestSpecificMappingInfoComparator
  • 並採取的第一個

分選以這種方式工作:在第一RequestSpecificMappingInfoComparator路徑與AntPathMatcher的幫助下進行比較,如果兩種方法根據本相等,則其他指標(例如參數,數,標頭數等)都考慮到與尊重請求。

+4

哇...我看看'resolveHandlerMethod(...)',談論超高迴圈複雜度代碼,我在第n個嵌套if語句後迷路了。我閱讀了'RequestSpecificMappingInfoComparator'上的javadoc,它討論了訂單列表。我很好奇爲什麼他們在GET和POST方法中表現不一樣。在另一個詞,如果我刪除映射#2,爲什麼映射#1不處理'/組/ 1 GET'而是春天拋出一個異常... – limc 2012-03-16 16:00:12

+0

@Ralph - 內部工作機制的很好的解釋 – raddykrish 2012-03-17 16:51:47

2

Spring試圖找到匹配最近的映射。
因此,就您的任何POST請求而言,爲請求類型找到的唯一映射是映射#3。 映射1或映射2都不匹配您的請求類型,因此將被忽略。 也許你可以嘗試移除映射#3,並且看到Spring拋出運行時錯誤,因爲它找不到匹配!

+1

我最初也認爲春天正在找到最接近的匹配。然而,我意識到這不是完全正確的,因爲如果是這樣的話,我應該能夠移除映射#2,並且'/ groups/1 GET'應該通過映射#1來處理,因爲它是最接近的匹配。 ..但我在這裏得到一個「找不到映射」異常。我找不到任何有關這種情況的更多的Spring文檔。 – limc 2012-03-16 14:04:08

-2

添加@PathVariable到龍ID參數映射#2

1

我想補充一個PUT映射/組/(編號)。我猜POST會起作用,但從HTTP角度來看並不嚴格正確。

加入@RequestMapping(「/ {id}」,POST)應該覆蓋它嗎?

+0

如何在不使用AJAX調用的情況下使用PUT提交Web表單?我仍然有興趣知道爲什麼Spring會以我目前的狀況如此行事。 – limc 2012-03-16 14:29:06

+0

也許還檢查瞭如何嘲笑與spring.http一個PUT://stackoverflow.com/questions/4362791/can-spring-mvc-handle-requests-from-html-forms-other-then-post-and-get – 2012-03-16 14:31:54

+0

您沒有爲組/ {id}映射定義的處理程序。我也認爲它是一個錯誤,因爲我很難看到你所看到的行爲的用例。 – 2012-03-16 14:46:30