Автор оригинала: Eugen Paraschiv.
1. Обзор
В этом уроке мы сосредоточимся на одной из основных аннотаций в Spring MVC: @RequestMapping.
Проще говоря, аннотация используется для сопоставления веб-запросов с методами контроллера Spring.
Дальнейшее чтение:
Обслуживайте статические ресурсы с помощью Spring
Начало работы с формами в Spring MVC
Конвертеры Http-сообщений с помощью Spring Framework
2. Основы @RequestMapping
Давайте начнем с простого примера: сопоставление HTTP-запроса с методом с использованием некоторых основных критериев.
2.1. @RequestMapping — по пути
@RequestMapping(value = "/ex/foos", method = RequestMethod.GET) @ResponseBody public String getFoosBySimplePath() { return "Get some Foos"; }
Чтобы проверить это сопоставление с помощью простой команды curl , выполните:
curl -i http://localhost:8080/spring-rest/ex/foos
2.2. @RequestMapping — метод HTTP
Параметр HTTP method не имеет значения по умолчанию. Итак, если мы не зададим значение, оно будет сопоставлено с любым HTTP-запросом.
Вот простой пример, аналогичный предыдущему, но на этот раз сопоставленный с запросом HTTP POST:
@RequestMapping(value = "/ex/foos", method = POST) @ResponseBody public String postFoos() { return "Post some Foos"; }
Чтобы проверить СООБЩЕНИЕ с помощью команды curl :
curl -i -X POST http://localhost:8080/spring-rest/ex/foos
3. Сопоставление запросов и заголовки HTTP
3.1. @RequestMapping С атрибутом headers
Сопоставление можно сузить еще больше, указав заголовок для запроса:
@RequestMapping(value = "/ex/foos", headers = "key=val", method = GET) @ResponseBody public String getFoosWithHeader() { return "Get some Foos with Header"; }
Чтобы проверить эту операцию, мы собираемся использовать поддержку заголовка curl :
curl -i -H "key:val" http://localhost:8080/spring-rest/ex/foos
и даже несколько заголовков через headers атрибут @RequestMapping :
@RequestMapping( value = "/ex/foos", headers = { "key1=val1", "key2=val2" }, method = GET) @ResponseBody public String getFoosWithHeaders() { return "Get some Foos with Header"; }
Мы можем проверить это с помощью команды:
curl -i -H "key1:val1" -H "key2:val2" http://localhost:8080/spring-rest/ex/foos
Обратите внимание, что для синтаксиса curl двоеточие разделяет ключ заголовка и значение заголовка, как и в спецификации HTTP, в то время как в Spring используется знак равенства.
3.2. @RequestMapping Потребляет и производит
Особого внимания заслуживает отображение типов носителей, создаваемых контроллером методом.
Мы можем сопоставить запрос на основе его заголовка Accept с помощью атрибута @RequestMapping | headers , введенного выше:
@RequestMapping( value = "/ex/foos", method = GET, headers = "Accept=application/json") @ResponseBody public String getFoosAsJsonFromBrowser() { return "Get some Foos with Header Old"; }
Сопоставление для этого способа определения заголовка Accept является гибким — он использует contains вместо equals, поэтому запрос, такой как следующий, все равно будет отображаться правильно:
curl -H "Accept:application/json,text/html" http://localhost:8080/spring-rest/ex/foos
Начиная с Spring 3.1, в аннотации @RequestMapping теперь есть производит и потребляет атрибуты , специально для этой цели:
@RequestMapping( value = "/ex/foos", method = RequestMethod.GET, produces = "application/json" ) @ResponseBody public String getFoosAsJsonFromREST() { return "Get some Foos with Header New"; }
Кроме того, старый тип сопоставления с атрибутом headers будет автоматически преобразован в новый механизм produces , начиная с Spring 3.1, поэтому результаты будут идентичны.
Это потребляется через curl таким же образом:
curl -H "Accept:application/json" http://localhost:8080/spring-rest/ex/foos
Кроме того, производит также поддерживает несколько значений:
@RequestMapping( value = "/ex/foos", method = GET, produces = { "application/json", "application/xml" } )
Имейте в виду, что это — старый и новый способы указания заголовка Accept — в основном одно и то же сопоставление, поэтому Spring не позволит им объединяться.
Если оба этих метода будут активны, это приведет к:
Caused by: java.lang.IllegalStateException: Ambiguous mapping found. Cannot map 'fooController' bean method java.lang.String org.baeldung.spring.web.controller .FooController.getFoosAsJsonFromREST() to { [/ex/foos], methods=[GET],params=[],headers=[], consumes=[],produces=[application/json],custom=[] }: There is already 'fooController' bean method java.lang.String org.baeldung.spring.web.controller .FooController.getFoosAsJsonFromBrowser() mapped.
Заключительное замечание о новых создает и потребляет механизмы, которые ведут себя иначе, чем большинство других аннотаций: Когда они указаны на уровне типа, аннотации на уровне метода не дополняют, а переопределяют информацию на уровне типа.
И, конечно, если вы хотите углубиться в создание API REST с Spring, проверьте новый курс REST с Spring .
4. Сопоставление Запросов С Переменными Пути
Части URI отображения могут быть привязаны к переменным с помощью аннотации @PathVariable .
4.1. Single @PathVariable
Простой пример с одной переменной пути:
@RequestMapping(value = "/ex/foos/{id}", method = GET) @ResponseBody public String getFoosBySimplePathWithPathVariable( @PathVariable("id") long id) { return "Get a specific Foo with id=" + id; }
Это можно проверить с помощью curl :
curl http://localhost:8080/spring-rest/ex/foos/1
Если имя параметра метода точно совпадает с именем переменной path, то это можно упростить, используя | @PathVariable без значения :
@RequestMapping(value = "/ex/foos/{id}", method = GET) @ResponseBody public String getFoosBySimplePathWithPathVariable( @PathVariable String id) { return "Get a specific Foo with id=" + id; }
Обратите внимание, что @PathVariable извлекает выгоду из автоматического преобразования типов, поэтому мы могли бы также объявить id как:
@PathVariable long id
4.2. Несколько @PathVariable
Более сложный URI может потребоваться сопоставить несколько частей URI с несколькими значениями :
@RequestMapping(value = "/ex/foos/{fooid}/bar/{barid}", method = GET) @ResponseBody public String getFoosBySimplePathWithPathVariables (@PathVariable long fooid, @PathVariable long barid) { return "Get a specific Bar with id=" + barid + " from a Foo with id=" + fooid; }
Это легко проверить с помощью curl таким же образом:
curl http://localhost:8080/spring-rest/ex/foos/1/bar/2
4.3. @PathVariable С Регулярным Выражением
Регулярные выражения также можно использовать при сопоставлении @PathVariable.
Например, мы ограничим отображение только числовыми значениями для id :
@RequestMapping(value = "/ex/bars/{numericId:[\\d]+}", method = GET) @ResponseBody public String getBarsBySimplePathWithPathVariable( @PathVariable long numericId) { return "Get a specific Bar with id=" + numericId; }
Это будет означать, что следующие URL-адреса будут совпадать:
http://localhost:8080/spring-rest/ex/bars/1
Но этого не будет:
http://localhost:8080/spring-rest/ex/bars/abc
5. Сопоставление Запросов С Параметрами Запроса
@RequestMapping позволяет легко сопоставлять параметры URL-адреса с @RequestParam аннотацией .
Теперь мы сопоставляем запрос с URI:
http://localhost:8080/spring-rest/ex/bars?id=100
@RequestMapping(value = "/ex/bars", method = GET) @ResponseBody public String getBarBySimplePathWithRequestParam( @RequestParam("id") long id) { return "Get a specific Bar with id=" + id; }
Затем мы извлекаем значение параметра id с помощью аннотации @RequestParam(“id”) в сигнатуре метода контроллера.
Чтобы отправить запрос с параметром id , мы будем использовать поддержку параметров в curl :
curl -i -d id=100 http://localhost:8080/spring-rest/ex/bars
В этом примере параметр был привязан напрямую, без предварительного объявления.
Для более сложных сценариев @RequestMapping может дополнительно определить параметры как еще один способ сужения сопоставления запросов:
@RequestMapping(value = "/ex/bars", params = "id", method = GET) @ResponseBody public String getBarBySimplePathWithExplicitRequestParam( @RequestParam("id") long id) { return "Get a specific Bar with id=" + id; }
Допускаются еще более гибкие сопоставления. Можно задать несколько значений params , и не все из них должны использоваться:
@RequestMapping( value = "/ex/bars", params = { "id", "second" }, method = GET) @ResponseBody public String getBarBySimplePathWithExplicitRequestParams( @RequestParam("id") long id) { return "Narrow Get a specific Bar with id=" + id; }
И, конечно же, запрос к URI, такому как:
http://localhost:8080/spring-rest/ex/bars?id=100&second=something
всегда будет отображаться на лучшее совпадение, которое является более узким совпадением, которое определяет как id , так и второй параметр.
6. Угловые случаи сопоставления запросов
6.1. @RequestMapping — Несколько путей, сопоставленных одному и тому же методу контроллера
Хотя для одного метода контроллера обычно используется одно значение @RequestMapping path (просто хорошая практика, а не жесткое и быстрое правило), в некоторых случаях может потребоваться сопоставление нескольких запросов с одним и тем же методом.
В этом случае значение атрибута @RequestMapping принимает несколько сопоставлений , а не только одно:
@RequestMapping( value = { "/ex/advanced/bars", "/ex/advanced/foos" }, method = GET) @ResponseBody public String getFoosOrBarsByPath() { return "Advanced - Get some Foos or Bars"; }
Теперь обе эти команды curl должны использовать один и тот же метод:
curl -i http://localhost:8080/spring-rest/ex/advanced/foos curl -i http://localhost:8080/spring-rest/ex/advanced/bars
6.2. @RequestMapping — Несколько методов HTTP-запроса к одному и тому же методу контроллера
Несколько запросов, использующих разные HTTP-глаголы, могут быть сопоставлены одному и тому же методу контроллера:
@RequestMapping( value = "/ex/foos/multiple", method = { RequestMethod.PUT, RequestMethod.POST } ) @ResponseBody public String putAndPostFoos() { return "Advanced - PUT and POST within single method"; }
С помощью curl оба они теперь будут использовать один и тот же метод:
curl -i -X POST http://localhost:8080/spring-rest/ex/foos/multiple curl -i -X PUT http://localhost:8080/spring-rest/ex/foos/multiple
6.3. @RequestMapping — резервный вариант для всех запросов
Чтобы реализовать простой резерв для всех запросов, использующих определенный метод HTTP, например, для GET:
@RequestMapping(value = "*", method = RequestMethod.GET) @ResponseBody public String getFallback() { return "Fallback for GET Requests"; }
или даже для всех запросов:
@RequestMapping( value = "*", method = { RequestMethod.GET, RequestMethod.POST ... }) @ResponseBody public String allFallback() { return "Fallback for All Requests"; }
6.4. Неоднозначная ошибка отображения
Ошибка неоднозначного сопоставления возникает, когда Spring оценивает два или более сопоставления запросов как одинаковые для разных методов контроллера. Сопоставление запросов одно и то же, если оно имеет один и тот же метод HTTP, URL-адрес, параметры, заголовки и тип носителя.
Например, это неоднозначное отображение:
@GetMapping(value = "foos/duplicate" ) public String duplicate() { return "Duplicate"; } @GetMapping(value = "foos/duplicate" ) public String duplicateEx() { return "Duplicate"; }
Вызванное исключение обычно содержит сообщения об ошибках в следующем порядке:
Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'fooMappingExamplesController' method public java.lang.String org.baeldung.web.controller.FooMappingExamplesController.duplicateEx() to {[/ex/foos/duplicate],methods=[GET]}: There is already 'fooMappingExamplesController' bean method public java.lang.String org.baeldung.web.controller.FooMappingExamplesController.duplicate() mapped.
Внимательное прочтение сообщения об ошибке указывает на то, что Spring не может сопоставить метод org.baeldung.web.controller.FooMappingExamplesController.duplicateEx(), поскольку он имеет конфликтное сопоставление с уже сопоставленным org.baeldung.web.controller.FooMappingExamplesController.duplicate().
Приведенный ниже фрагмент кода не приведет к неоднозначной ошибке сопоставления, поскольку оба метода возвращают разные типы контента :
@GetMapping(value = "foos/duplicate", produces = MediaType.APPLICATION_XML_VALUE) public String duplicateXml() { return "Duplicate "; } @GetMapping(value = "foos/duplicate", produces = MediaType.APPLICATION_JSON_VALUE) public String duplicateJson() { return "{\"message\":\"Duplicate\"}"; }
Это различие позволяет нашему контроллеру возвращать правильное представление данных на основе заголовка Accepts , предоставленного в запросе.
Другой способ решить эту проблему-обновить URL-адрес, назначенный любому из двух задействованных методов.
7. Новые ярлыки сопоставления запросов
Spring Framework 4.3 представила несколько новых аннотаций сопоставления HTTP, все они основаны на @RequestMapping :
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
- @@Pathmapping
Эти новые аннотации могут улучшить читабельность и уменьшить многословие кода.
Давайте рассмотрим эти новые аннотации в действии, создав RESTful API, поддерживающий операции CRUD:
@GetMapping("/{id}") public ResponseEntity> getBazz(@PathVariable String id){ return new ResponseEntity<>(new Bazz(id, "Bazz"+id), HttpStatus.OK); } @PostMapping public ResponseEntity> newBazz(@RequestParam("name") String name){ return new ResponseEntity<>(new Bazz("5", name), HttpStatus.OK); } @PutMapping("/{id}") public ResponseEntity> updateBazz( @PathVariable String id, @RequestParam("name") String name) { return new ResponseEntity<>(new Bazz(id, name), HttpStatus.OK); } @DeleteMapping("/{id}") public ResponseEntity> deleteBazz(@PathVariable String id){ return new ResponseEntity<>(new Bazz(id), HttpStatus.OK); }
Глубокое погружение в них можно найти здесь .
8. Конфигурация Пружины
Конфигурация Spring MVC достаточно проста, учитывая, что наш FooController определен в следующем пакете:
package org.baeldung.spring.web.controller; @Controller public class FooController { ... }
Нам просто нужен класс @Configuration , чтобы включить полную поддержку MVC и настроить сканирование путей к классам для контроллера:
@Configuration @EnableWebMvc @ComponentScan({ "org.baeldung.spring.web.controller" }) public class MvcConfig { // }
9. Заключение
Эта статья была посвящена @RequestMapping аннотации в Spring , обсуждая простой случай использования, сопоставление заголовков HTTP, связывание частей URI с @PathVariable и работу с параметрами URI и аннотацией @RequestParam .
Если вы хотите узнать, как использовать другую основную аннотацию в Spring MVC, вы можете изучить аннотацию @ModelAttribute здесь .
Полный код из статьи доступен на GitHub .