«1. Обзор

В этом руководстве мы сосредоточимся на одной из основных аннотаций в Spring MVC: @RequestMapping.

Проще говоря, аннотация используется для сопоставления веб-запросов с методами Spring Controller.

2. Основы @RequestMapping

Давайте начнем с простого примера: сопоставление HTTP-запроса с методом с использованием некоторых основных критериев.

2.1. @RequestMapping — by Path

@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 не имеет значения по умолчанию. Итак, если мы не укажем значение, оно будет сопоставлено с любым HTTP-запросом.

Вот простой пример, похожий на предыдущий, но на этот раз сопоставленный с HTTP-запросом POST:

@RequestMapping(value = "/ex/foos", method = POST)
@ResponseBody
public String postFoos() {
    return "Post some Foos";
}

Чтобы протестировать POST с помощью команды curl:

curl -i -X POST http://localhost:8080/spring-rest/ex/foos

3. RequestMapping и 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 Consumes and Produces

Сопоставление типов мультимедиа, созданных методом контроллера, заслуживает особого внимания.

Мы можем сопоставить запрос на основе его заголовка Accept с помощью атрибута заголовков @RequestMapping, представленного выше: таким образом, запрос, подобный следующему, по-прежнему будет отображаться правильно:

@RequestMapping(
  value = "/ex/foos", 
  method = GET, 
  headers = "Accept=application/json")
@ResponseBody
public String getFoosAsJsonFromBrowser() {
    return "Get some Foos with Header Old";
}

Начиная с Spring 3.1 аннотация @RequestMapping теперь имеет атрибуты производства и потребления, специально для этой цели:

curl -H "Accept:application/json,text/html" 
  http://localhost:8080/spring-rest/ex/foos

Кроме того, старый Тип сопоставления с атрибутом заголовков будет автоматически преобразован в новый механизм, начиная с Spring 3.1, поэтому результаты будут идентичными.

@RequestMapping(
  value = "/ex/foos", 
  method = RequestMethod.GET, 
  produces = "application/json"
)
@ResponseBody
public String getFoosAsJsonFromREST() {
    return "Get some Foos with Header New";
}

Это используется через curl таким же образом:

Кроме того, products также поддерживает несколько значений:

curl -H "Accept:application/json" 
  http://localhost:8080/spring-rest/ex/foos

Имейте в виду, что это — старый и новый способы указания Заголовок Accept — в основном одно и то же сопоставление, поэтому Spring не позволит использовать их вместе.

@RequestMapping(
  value = "/ex/foos", 
  method = GET,
  produces = { "application/json", "application/xml" }
)

Активация обоих этих методов приведет к:

Последнее замечание о новых механизмах создания и потребления, которые ведут себя иначе, чем большинство других аннотаций: Когда они указаны на уровне типа, аннотации на уровне метода не дополнять, а переопределять информацию на уровне типа.

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.

И, конечно же, если вы хотите глубже изучить создание REST API с помощью Spring, ознакомьтесь с новым курсом REST with Spring.

4. RequestMapping с переменными пути

Части URI сопоставления могут быть привязаны к переменным с помощью аннотации @PathVariable.

4.1. Single @PathVariable

Простой пример с одной переменной пути:

Это можно проверить с помощью curl:

@RequestMapping(value = "/ex/foos/{id}", method = GET)
@ResponseBody
public String getFoosBySimplePathWithPathVariable(
  @PathVariable("id") long id) {
    return "Get a specific Foo with id=" + id;
}

Если имя параметра метода точно соответствует имени переменной пути, тогда это можно упростить, используя @PathVariable без значения:

curl http://localhost:8080/spring-rest/ex/foos/1

Обратите внимание, что @PathVariable выигрывает от автоматического преобразования типов, поэтому мы могли бы также объявить идентификатор как:

@RequestMapping(value = "/ex/foos/{id}", method = GET)
@ResponseBody
public String getFoosBySimplePathWithPathVariable(
  @PathVariable String id) {
    return "Get a specific Foo with id=" + id;
}

4.2. Multiple @PathVariable

@PathVariable long id

В более сложном URI может потребоваться сопоставление нескольких частей URI с несколькими значениями:

Это легко проверить с помощью curl таким же образом:

@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;
}

4.3. @PathVariable с регулярным выражением

curl http://localhost:8080/spring-rest/ex/foos/1/bar/2

Регулярные выражения также можно использовать при отображении @PathVariable.

Например, мы ограничим отображение, чтобы принимать только числовые значения для идентификатора:

Это будет означать, что следующие URI будут совпадать:

@RequestMapping(value = "/ex/bars/{numericId:[\\d]+}", method = GET)
@ResponseBody
public String getBarsBySimplePathWithPathVariable(
  @PathVariable long numericId) {
    return "Get a specific Bar with id=" + numericId;
}

Но это не будет:

http://localhost:8080/spring-rest/ex/bars/1

5. RequestMapping с параметрами запроса

http://localhost:8080/spring-rest/ex/bars/abc

@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;
}

Допускаются еще более гибкие сопоставления. Можно задать несколько значений параметров, и не обязательно использовать все из них:

@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

, всегда будет сопоставлен с наилучшим соответствием â €», что является более узким соответствием, определяющим как идентификатор, так и второй параметр.

6. Угловые случаи RequestMapping

6.1. @RequestMapping — несколько путей сопоставляются с одним и тем же методом контроллера

Хотя одно значение пути @RequestMapping обычно используется для одного метода контроллера (просто хорошая практика, а не жесткое и быстрое правило), в некоторых случаях сопоставление может потребоваться несколько запросов к одному и тому же методу.

В этом случае атрибут value @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 "<message>Duplicate</message>";
}
    
@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 @PatchMapping

Эти новые аннотации могут улучшить удобочитаемость и уменьшить многословность кода.

Давайте посмотрим на эти новые аннотации в действии, создав 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

Конфигурация Spring MVC достаточно проста, учитывая, что наш FooController определен в следующем пакете:

package org.baeldung.spring.web.controller;

@Controller
public class FooController { ... }

Нам просто нужен класс @Configuration, чтобы включить полную поддержку MVC и настроить сканирование classpath для контроллера:

@Configuration
@EnableWebMvc
@ComponentScan({ "org.baeldung.spring.web.controller" })
public class MvcConfig {
    //
}

9. Заключение

Эта статья посвящена аннотации @RequestMapping в Spring, обсуждению простого варианта использования, отображению заголовков HTTP, связыванию частей URI с @PathVariable, и работа с параметрами URI и аннотацией @RequestParam.

Если вы хотите узнать, как использовать другую основную аннотацию в Spring MVC, вы можете изучить аннотацию @ModelAttribute здесь.

«Полный код из статьи доступен на GitHub.