Понимание того, как работает 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)? Это имеет смысл, что он должен обрабатывать (/groups POST) здесь, поскольку сопоставление запроса соответствует URI. Зачем (/groups/1 POST) не вызывает ли здесь исключение "Картографирование не найдено"? Фактически, это почти похоже на любой POST с URI, начинающимся с /groups (например: /groups/bla/1 POST) также будет обработан отображением #3.

Может ли кто-нибудь дать мне четкое объяснение этого? Большое спасибо.

ПОЯСНЕНИЯ

Я понимаю тот факт, что я могу использовать более подходящие методы (например, GET, POST, PUT или DELETE)... или я могу создать еще одно сопоставление запроса для обработки /groups/{id} POST,

Однако то, что я действительно хочу знать, это...

.... "Почему отображение № 3 обрабатывает /groups/1 POST тоже?"

Рассуждения о "ближайшем совпадении", похоже, не верны, потому что, если я уберу отображение #2, то я думаю, что отображение #1 будет обрабатывать /groups/1 GET, но это не так, и это вызывает исключение "Нет сопоставления найдено".

Я просто немного озадачен здесь.

4 ответа

Решение

Это сложно, я думаю, что лучше читать код.

Весной 3.0 Волшебство сделано методом public Method resolveHandlerMethod(HttpServletRequest request) внутреннего класса ServletHandlerMethodResolver из org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter,

Экземпляр этого класса существует для каждого класса контроллера запросов и имеет поле handlerMethods который содержит список всех методов запроса.

Но позвольте мне обобщить, как я понимаю

  • Spring сначала проверяет, соответствует ли хотя бы один метод-обработчик (это может содержать ложные отрицания)
  • Затем он создает карту всех действительно соответствующих методов-обработчиков.
  • Затем он сортирует карту по пути запроса: RequestSpecificMappingInfoComparator
  • и берет первый

Сортировка работает следующим образом: RequestSpecificMappingInfoComparator сначала сравнивает путь с помощью AntPathMatcherесли два метода в соответствии с этим равны, то в отношении запроса учитываются другие метрики (например, количество параметров, количество заголовков и т. д.).

Spring пытается найти отображение, которое соответствует ближайшему.
Следовательно, в вашем случае любого запроса POST единственной картой, найденной для типа запроса, является Mapping# 3. Ни Mapping 1, ни Mapping 2 не соответствуют вашему типу запроса и, следовательно, игнорируются. Может быть, вы можете попытаться удалить Mapping # 3 и увидеть, что Spring выдает ошибку времени выполнения, так как не находит соответствия!

Я бы добавил отображение PUT для /groups/{id}. Я думаю, что POST тоже будет работать, но не совсем правильно с точки зрения HTTP.

добавление @RequestMapping("/{id}", POST) должно покрыть это?

Добавьте @PathVariable к параметру Long id в отображении #2

Другие вопросы по тегам