Post

Spring RequestMapping

1. @RequestMapping 기초

몇 가지 기본 기준을 사용하여 HTTP 요청을 메서드에 매핑하는 간단한 예이다. Spring이 기본적으로 루트 컨텍스트 경로 (“/”)에서 콘텐츠를 제공한다고 가정한다. 이 문서의 모든 CURL 요청은 기본 루트 컨텍스트 경로에 의존한다.

1) @RequestMapping - 경로별

1
2
3
4
5
@RequestMapping(value = "/ex/foos", method = RequestMethod.GET)
@ResponseBody
public String getFoosBySimplePath() {
    return "Get some Foos";
}

curl 명령으로 이 매핑을 테스트하려면 다음을 실행한다.

1
curl -i http://localhost:8080/ex/foos

2) @RequestMapping - HTTP 메서드

HTTP 메소드 매개변수에는 기본값이 없다. 따라서 값을 지정하지 않으면 모든 HTTP 요청에 매핑된다.

이전 예제와 유사하지만 HTTP POST 요청에 매핑된 간단한 예제이다.

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

curl 명령을 통해 POST를 테스트하려면 다음을 실행한다.

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

2. RequestMapping 및 HTTP 헤더

1) @RequestMapping 헤더 속성 사용

요청에 대한 헤더를 지정하여 매핑을 더욱 좁힐 수 있다.

1
2
3
4
5
@RequestMapping(value = "/ex/foos", headers = "key=val", method = GET)
@ResponseBody
public String getFoosWithHeader() {
    return "Get some Foos with Header";
}

테스트하기 위해 curl 헤더 지원을 사용한다.

1
curl -i -H "key:val" http://localhost:8080/ex/foos

@RequestMapping의 headers 속성을 통해 여러 헤더를 사용할 수도 있다.

1
2
3
4
5
@RequestMapping(value = "/ex/foos", headers = { "key1=val1", "key2=val2" }, method = GET)
@ResponseBody
public String getFoosWithHeaders() {
    return "Get some Foos with Header";
}

다음 명령으로 테스트할 수 있다.

1
curl -i -H "key1:val1" -H "key2:val2" http://localhost:8080/ex/foos

curl 구문의 경우 HTTP 사양에서와 같이 콜론으로 헤더 키와 헤더 값을 구분하지만 Spring에서는 등호를 사용한다.

2) @RequestMapping 소비 및 생산

컨트롤러 메서드로 생성된 미디어 유형 매핑은 특별한 주의가 필요하다.

@RequestMapping 헤더 속성을 통해 Accept 헤더를 기반으로 요청을 매핑할 수 있다.

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

Accept 헤더를 정의하는 이 방법에 대한 일치는 유연하다. equals 대신에 contains를 사용하므로 다음과 같은 요청은 여전히 ​​올바르게 매핑된다.

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

Spring 3.1부터 @RequestMapping 어노테이션은 이러한 목적을 위해 생성 및 소비 속성을 갖는다.

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

또한 headers 속성을 사용한 이전 유형의 매핑은 Spring 3.1부터 새로운 생성 메커니즘으로 자동 변환되므로 결과는 동일하다.

이것은 같은 방식으로 curl을 통해 소비된다.

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

또한 생성은 여러 값도 지원한다.

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

Accept 헤더를 지정하는 이전 방식과 새로운 방식은 기본적으로 동일한 매핑이므로 Spring은 이를 함께 허용하지 않는다.

이 두 가지 방법을 모두 활성화하면 다음과 같은 결과가 나타난다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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.

대부분의 다른 주석과 다르게 동작하는 새로운 생성 및 소비 메커니즘에 대한 마지막 참고 사항: 유형 수준에서 지정되면 메서드 수준 주석은 보완하지 않고 유형 수준 정보를 재정의한다.

3. 경로 변수를 사용한 RequestMapping

매핑 URI의 일부는 @PathVariable 주석을 통해 변수에 바인딩될 수 있다.

1) 단일 @PathVariable

단일 경로 변수가 있는 간단한 예이다.

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

curl로 테스트할 수 있다.

1
curl http://localhost:8080/ex/foos/1

메서드 매개 변수의 이름이 경로 변수의 이름과 정확히 일치하는 경우 값 없이 @PathVariable을 사용하여 단순화할 수 있다.

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

@PathVariable은 자동 유형 변환의 이점이 있으므로 id를 다음과 같이 선언할 수도 있다.

1
@PathVariable long id

2) 다중 @PathVariable

더 복잡한 URI는 URI의 여러 부분을 여러 값 에 매핑해야 할 수 있다.

1
2
3
4
5
@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;
}

이것은 같은 방식으로 컬을 사용하여 쉽게 테스트할 수 있다.

1
curl http://localhost:8080/ex/foos/1/bar/2

3) 정규식을 사용한 @PathVariable

@PathVariable을 매핑할 때 정규식을 사용할 수도 있다.

예를 들어 id에 대한 숫자 값만 허용하도록 매핑을 제한한다.

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

이는 다음 URI가 일치함을 의미한다.

1
http://localhost:8080/ex/bars/1

그러나 이것은 다음을 수행하지 않는다.

1
http://localhost:8080/ex/bars/abc

4. 요청 매개변수를 사용한 요청 매핑

@RequestMapping을 사용하면 @RequestParam 주석을 사용하여 URL 매개변수를 쉽게 매핑할 수 있다.

요청을 URI에 매핑한다.

1
http://localhost:8080/ex/bars?id=100
1
2
3
4
5
@RequestMapping(value = "/ex/bars", method = GET)
@ResponseBody
public String getBarBySimplePathWithRequestParam(@RequestParam("id") long id) {
    return "Get a specific Bar with id=" + id;
}

그런 다음 컨트롤러 메서드 서명에서 @RequestParam("id") 주석을 사용하여 id 매개 변수의 값을 추출한다.

id 매개변수로 요청을 보내려면 curl에서 매개변수 지원을 사용한다.

1
curl -i -d id=100 http://localhost:8080/ex/bars

이 예에서 매개변수는 먼저 선언되지 않고 직접 바인딩되었다.

고급 시나리오의 경우 @RequestMapping은 요청 매핑을 좁히는 또 다른 방법으로 매개 변수를 선택적으로 정의할 수 있다.

1
2
3
4
5
@RequestMapping(value = "/ex/bars", params = "id", method = GET)
@ResponseBody
public String getBarBySimplePathWithExplicitRequestParam(@RequestParam("id") long id) {
    return "Get a specific Bar with id=" + id;
}

훨씬 더 유연한 매핑이 허용된다. 여러 매개변수 값을 설정할 수 있으며 모두 사용할 필요는 없다.

1
2
3
4
5
@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에 대한 요청도 있다.

1
http://localhost:8080/ex/bars?id=100&second=something

항상 가장 일치하는 항목에 매핑된다. 이는 id와 두 번째 매개변수를 모두 정의하는 더 좁은 일치 항목이다.

5. RequestMapping 코너 케이스

1) @RequestMapping - 동일한 컨트롤러 메서드에 매핑된 여러 경로

일반적 으로 단일 @RequestMapping 경로 값이 단일 컨트롤러 메서드에 사용되지만(엄격하고 빠른 규칙이 아닌 모범 사례일 뿐임) 여러 요청을 동일한 메서드에 매핑해야 하는 경우가 있다.

이 경우 @RequestMapping의 값 속성은 단일 매핑이 아닌 여러 매핑을 허용한다.

1
2
3
4
5
@RequestMapping(value = { "/ex/advanced/bars", "/ex/advanced/foos" }, method = GET)
@ResponseBody
public String getFoosOrBarsByPath() {
    return "Advanced - Get some Foos or Bars";
}

이제 이 두 컬 명령은 모두 동일한 메서드에 도달해야 한다.

1
2
curl -i http://localhost:8080/ex/advanced/foos
curl -i http://localhost:8080/ex/advanced/bars

2) @RequestMapping - 동일한 컨트롤러 메서드에 대한 여러 HTTP 요청 메서드

서로 다른 HTTP 동사를 사용하는 여러 요청을 동일한 컨트롤러 메서드에 매핑할 수 있다.

1
2
3
4
5
@RequestMapping(value = "/ex/foos/multiple", method = { RequestMethod.PUT, RequestMethod.POST })
@ResponseBody
public String putAndPostFoos() {
    return "Advanced - PUT and POST within single method";
}

curl을 사용하면 이제 두 가지 모두 동일한 방법을 사용한다.

1
2
curl -i -X POST http://localhost:8080/ex/foos/multiple
curl -i -X PUT http://localhost:8080/ex/foos/multiple

3) @RequestMapping - 모든 요청에 ​​대한 폴백

GET에 대해 특정 HTTP 메서드를 사용하는 모든 요청에 ​​대해 간단한 폴백을 구현하려면 다음을 수행한다.

1
2
3
4
5
@RequestMapping(value = "*", method = RequestMethod.GET)
@ResponseBody
public String getFallback() {
    return "Fallback for GET Requests";
}

모든 요청에 ​​대해서도 다음을 수행한다.

1
2
3
4
5
@RequestMapping(value = "*", method = { RequestMethod.GET, RequestMethod.POST ... })
@ResponseBody
public String allFallback() {
    return "Fallback for All Requests";
}

4) 모호한 매핑 오류

모호한 매핑 오류는 Spring이 서로 다른 컨트롤러 메서드에 대해 둘 이상의 요청 매핑을 동일하게 평가할 때 발생한다. HTTP 메서드, URL, 매개변수, 헤더 및 미디어 유형이 동일한 요청 매핑은 동일하다.

예를 들어 다음은 모호한 매핑이다.

1
2
3
4
5
6
7
8
9
@GetMapping(value = "foos/duplicate" )
public String duplicate() {
    return "Duplicate";
}

@GetMapping(value = "foos/duplicate" )
public String duplicateEx() {
    return "Duplicate";
}

throw된 예외에는 일반적으로 다음과 같은 오류 메시지가 있다.

1
2
3
4
5
6
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.duplicate()와 충돌하는 매핑이 있으므로 org.baeldung.web.controller.FooMappingExamplesController.duplicateEx() 메서드를 매핑할 수 없다는 사실을 지적한다.

아래 코드는 두 메서드가 서로 다른 콘텐츠 유형을 반환하기 때문에 모호한 매핑 오류가 발생하지 않는다.

1
2
3
4
5
6
7
8
9
@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을 업데이트하는 것이다.

6. 새로운 요청 매핑 바로 가기

Spring Framework 4.3은 모두 @RequestMapping에 기반한 몇 가지 새로운 HTTP 매핑 주석을 도입했다.

  • @GetMapping

  • @PostMapping

  • @PutMapping

  • @DeleteMapping

  • @PatchMapping

이러한 새로운 주석은 가독성을 향상시키고 코드의 장황함을 줄일 수 있다.

CRUD 작업을 지원하는 RESTful API를 생성하여 작동 중인 이러한 새 주석을 확인한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@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);
}

자세한 내용은 여기에서 확인할 수 있다.

7. 스프링 구성

FooController가 다음 패키지에 정의되어 있다는 점을 고려하면 Spring MVC 구성은 충분히 간단하다.

1
2
3
4
package org.baeldung.spring.web.controller;

@Controller
public class FooController { ... }

전체 MVC 지원을 활성화하고 컨트롤러에 대한 클래스 경로 검색을 구성하려면 @Configuration 클래스가 필요하다.

1
2
3
4
5
6
@Configuration
@EnableWebMvc
@ComponentScan({ "org.baeldung.spring.web.controller" })
public class MvcConfig {
    //
}

[출처 및 참고]

This post is licensed under CC BY 4.0 by the author.