Spring Web Reactive | 1. Spring WebFlux | 1.4. Annotated Controllers #1
Spring WebFlux는 어노테이션 기반 프로그래밍 모델을 제공한다. @Controller
, @RestController
컨포넌트는 어노테이션을 사용하여 요청 매핑, 요청 입력, 예외 처리 등을 표현한다. 어노테이션이 선언된 컨트롤러는 유연한 메서드 시그너처가 기본 클래스를 상속하거나 특정 인터페이스를 구현하거나 할 필요는 없다.
다음 목록은 기본 예제를 보여준다.
Java
@RestController
public class HelloController {
@GetMapping("/hello")
public String handle() {
return "Hello WebFlux";
}
}
Kotlin
@RestController
class HelloController {
@GetMapping("/hello")
fun handle() = "Hello WebFlux"
}
위의 예제에서는 메서드는 응답 본문에 작성된 String
을 반환한다.
1.4.1. @Controller
표준 Spring Bean 정의를 사용하여 컨트롤러 Bean을 정의 할 수 있다. @Controller
스테레오 타입(stereotype)은 자동 검출하고, 클래스 경로에서 @Component
클래스를 감지하여, Bean 정의를 자동 등록하는 Spring 일반 지원과 연계되어 있다. 또한 어노테이션 클래스의 스테레오 타입으로서 기능하여 Web 컴포넌트로서의 역할을 보여준다.
그런 @Controller
Bean의 자동 감지 기능을 활성화 하려면, 다음 예제와 같이 Java 구성에 컴포넌트 스캐닝를 추가 할 수 있다.
Java
@Configuration
@ComponentScan("org.example.web") // (1)
public class WebConfig {
// ...
}
Kotlin
@Configuration
@ComponentScan("org.example.web") // (1)
class WebConfig {
// ...
}
- (1)
org.example.web
패키지를 검색한다.
@RestController
는그 자체가 @Controller
와 @ResponseBody
메타 어노테이션을 합한 어노테이션이며, 모든 메서드가 유형 레벨의 @ResponseBody
어노테이션을 상속하는 컨트롤러를 나타낸다. HTML 템플릿을 사용하여 뷰 리졸버와 렌더링에 대한 응답 본문에 직접 작성한다.
1.4.2. 요청 매핑(Request Mapping)
@RequestMapping
어노테이션은 요청을 컨트롤러 메서드에 매핑하는데 사용된다. URL, HTTP 메서드, 요청 매개 변수, 헤더, 미디어 형식과 일치하는 다양한 속성이 있다. 클래스 레벨에서 사용하여 공유 매핑을 표현하거나 메서드 레벨에서 사용하여 특정 엔드 포인트 매핑을 지정할 수도 있다.
@RequestMapping
HTTP 메서드 고유의 단축키 변형도 있다.
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
대부분의 컨트롤러 메서드는 디폴트로 모든 HTTP 메서드와 일치하는 @RequestMapping
를 사용하는 것이 아니라 특정 HTTP 메서드에 매핑 될 필요가 있기 때문에 위의 어노테이션은 커스텀 어노테이션 이다. 동시에 공유 매핑을 표현하려면 클래스 레벨에서 @RequestMapping
가 필요하다.
다음 예제에서는 유형 및 메서드 레벨의 매핑을 사용하고 있다.
Java
@RestController
@RequestMapping("/persons")
class PersonController {
@GetMapping("/{id}")
public Person getPerson(@PathVariable Long id) {
// ...
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public void add(@RequestBody Person person) {
// ...
}
}
Kotlin
@RestController
@RequestMapping("/persons")
class PersonController {
@GetMapping("/{id}")
fun getPerson(@PathVariable id: Long): Person {
// ...
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
fun add(@RequestBody person: Person) {
// ...
}
}
URI 패턴
glob 패턴과 와일드 카드를 사용하여 요청을 매핑 할 수 있다.
패턴 | 설명 | 샘플 |
---|---|---|
? |
1 문자와 일치 | /pages/t?st.html" 은 "/pages/test.html" , "/pages/t3st.html" 와 일치한다. |
* |
경로 세그먼트에서 0개 이상의 문자와 일치 | "/resources/*.png" 는 "/resources/file.png" 일치하고, "/projects/*/versions" 는 "/projects/spring/versions" 와 일치하지만 "/projects/spring/boot/versions" 은 일치하지 않는다. |
** |
경로의 끝까지 0개 이상의 경로 세그먼트에 일치 | "/resources/**" 은 "/resources/file.png" , "/resources/images/file.png" 일치하고, ** 경로의 마지막에서만 허용하고 있기 때문에 "/resources/**/file.png" 는 사용할 수 없다. |
{name} |
경로 세그먼트를 비교하여 “name"이라는 변수로 저장 | "/projects/{project}/versions" 는 "/projects/spring/versions" 일치하고, project=spring 로 변수 저장한다. |
{name:[a-z]+} |
정규 표현식 "[a-z]+" 을 “name"이라는 경로 변수로 저장 |
"/projects/{project:[a-z]+}/versions" 는 "/projects/spring/versions" 일치하지만 "/projects/spring1/versions" 은 매칭되지 않는다. |
{*path} |
경로의 마지막까지 0개 이상의 경로 세그먼트에 일치 “path"라는 변수로 저장 | "/resources/{*file}" 는 "/resources/images/file.png" 일치하고 file=images/file.png 로 변수 저장한다. |
다음의 예제와 같이, 저정한 URI 변수는 @PathVariable
을 사용하여 액세스 할 수 있다.
Java
@GetMapping("/owners/{ownerId}/pets/{petId}")
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
// ...
}
Kotlin
@GetMapping("/owners/{ownerId}/pets/{petId}")
fun findPet(@PathVariable ownerId: Long, @PathVariable petId: Long): Pet {
// ...
}
다음의 예제와 같이, 클래스 및 메서드 레벨에서 URI 변수를 선언 할 수 있다.
Java
@Controller
@RequestMapping("/owners/{ownerId}") // (1)
public class OwnerController {
@GetMapping("/pets/{petId}") // (2)
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
// ...
}
}
Kotlin
@Controller
@RequestMapping("/owners/{ownerId}") // (1)
class OwnerController {
@GetMapping("/pets/{petId}") // (2)
fun findPet(@PathVariable ownerId: Long, @PathVariable petId: Long): Pet {
// ...
}
}
- (1) 클래스 레벨의 URI 매핑.
- (2) 메서드 레벨의 URI 매핑.
URI 변수는 자동으로 적절한 형식으로 변환되거나, 변환되지 않으면 TypeMismatchException
이 발생한다. 단순형( int
, long
, Date
등)은 기본적으로 지원되며, 다른 데이터 형의 지원을 등록 할 수 있다. 형 변환과 DataBinder
를 참조해라.
URI 변수는 명시적으로 이름을 붙일 수 있다(예를 들면, @PathVariable("customId")
). 이름으면 생략 할 수 있다. 단, 컴파일할 때 디버깅 정보도 포함하거나 Java 8의 -parameters
컴파일러 플래그를 사용하여 코드를 컴파일해야 한다.
구문 {*varName}
은 0개 이상의 나머지 경로 세그먼트와 일치하는 URI 변수를 선언한다. 예를 들어, /resources/{*path}
는 /resources/
의 모든 파일과 일치하는"path"
변수는 전체 상대 경로가 저장된다.
구문 {varName:regex}
은 구문 {varName:regex}
을 가진 정규식 URI 변수를 선언한다. 예를 들어, /spring-web-3.0.5.jar
의 URL을 지정하면 다음의 메서드는 이름, 버전, 파일 확장자를 추출할수 있다.
Java
@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}")
public void handle(@PathVariable String version, @PathVariable String ext) {
// ...
}
Kotlin
@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}")
fun handle(@PathVariable version: String, @PathVariable ext: String) {
// ...
}
URI 경로 패턴은 시작할 때에 PropertyPlaceHolderConfigurer
를 통해 로컬, 시스템, 환경, 그 외에 속성 소스에 해결되는 ${...}
자리 표시자(Place Holder)를 포함 할 수 있다. 예를 들어, 이를 사용하여 외부 설정에 따라 기본 URL을 매개 변수화 할 수 있다.
Spring WebFlux는 URI 경로 일치 지원을 위해
PathPattern
,PathPatternParser
를 사용한다. 두 클래스는spring-web
에 런타임에 다수의 URI 경로 패턴이 일치하는 Web 어플리케이션의 HTTP URL 경로에서 사용하기 위해 특별히 설계되어 있다.
Spring WebFlux는 /person
등의 매핑 /person.*
과 일치하는 Spring MVC와 달리 접미사(suffix) 패턴 매칭을 지원하지 않는다. URL 기반의 콘텐츠 협상(content negotiation)은 필요에 따라 쿼리 매개 변수를 사용하는 것이 좋다. 쿼리 매개 변수는 더 단순하고 더 명시적 URL 경로 기반의 공격에 취약하지 않다.
패턴 비교(Pattern Comparison)
여러 패턴이 URL과 일치하는 경우, 비교하여 최적의 일치를 찾아야 한다. 이것은 보다 더 구체적인 패턴을 찾는 PathPattern.SPECIFICITY_COMPARATOR
으로 이루어진다.
모든 패턴에 대한 URI 변수와 와일드 카드의 숫자에 따라 점수가 계산된다. URI 변수의 점수는 와일드 카드보다 낮다. 총 점수가 낮은 패턴이 우선된다. 2개의 패턴의 점수가 동일한 경우, 긴 쪽이 선택된다.
포괄 패턴 (예를 들어 **
, {*varName}
)는 채점에서 제외되고 대신 항상 마지막으로 정렬된다. 두 패턴이 모두 포괄인 경우, 긴 쪽이 선택된다.
소비 가능한 미디어 유형(Consumable Media Types)
다음의 예제와 같이 요청의 Content-Type
에 따라 요청 매핑되는 범위를 줄일 수 있다.
Java
@PostMapping(path = "/pets", consumes = "application/json")
public void addPet(@RequestBody Pet pet) {
// ...
}
Kotlin
@PostMapping("/pets", consumes = ["application/json"])
fun addPet(@RequestBody pet: Pet) {
// ...
}
소비(consumes) 속성은 부정 표현도 지원하고 있다. 예를 들어, !text/plain
은 text/plain
이외의 콘텐츠 형식을 의미한다.
클래스 레벨에서 consumes
속성을 선언하여 클래스 전체에서 공유할 수도 있다. 단, 다른 대부분의 요청 매핑 속성과 달리 클래스 레벨에서 사용하는 경우, 메서드 레벨의 consumes
속성은 클래스 레벨의 선언을 계승하는 것이 아니라 우선시 한다.
MediaType
은APPLICATION_JSON_VALUE
,APPLICATION_XML_VALUE
등의 일반적으로 사용되는 미디어 유형에 상수를 제공한다.
생산 가능한 미디어 유형(Producible Media Types)
다음의 예와 같이, Accept 요청 헤더와 컨트롤러 메서드가 생성하는 콘텐츠 형식 목록에 따라 요청 매핑되는 범위를 줄일 수 있다.
Java
@GetMapping(path = "/pets/{petId}", produces = "application/json")
@ResponseBody
public Pet getPet(@PathVariable String petId) {
// ...
}
Kotlin
@GetMapping("/pets/{petId}", produces = ["application/json"])
@ResponseBody
fun getPet(@PathVariable String petId): Pet {
// ...
}
미디어 타입은 문자 세트을 지정할 수 있다. 부정 표현이 지원되고 있다. 예를 들어, !text/plain
는 text/plain
이외의 콘텐츠 형식을 의미한다.
클래스 레벨에서 produces
속성을 선언하여 클래스 전체에서 공유할 수도 있다. 단, 다른 대부분의 요청 매핑 속성과 달리 클래스 레벨에서 사용하는 경우, 메서드 레벨의 produces
속성은 클래스 레벨의 선언을 계승하는 것이 아니라 우선시 한다.
MediaType
일반적으로 사용되는 미디어 유형에 상수를 제공한다.APPLICATION_JSON_VALUE
,APPLICATION_XML_VALUE
매개 변수와 헤더
쿼리 매개 변수의 조건에 따라 요청 매핑되는 범위를 줄일 수 있다. 쿼리 매개 변수의 존재하거나(myParam
) 혹은 없거나 (!myParam
) 또는 특정 값 (myParam=myValue
)을 테스트 할 수 있다. 다음 예제에서는 값 매개 변수를 테스트한다.
Java
@GetMapping(path = "/pets/{petId}", params = "myParam=myValue") // (1)
public void findPet(@PathVariable String petId) {
// ...
}
Kotlin
@GetMapping("/pets/{petId}", params = ["myParam=myValue"]) // (1)
fun findPet(@PathVariable petId: String) {
// ...
}
- (1)
myParam
이myValue
와 같은지 확인해라.
다음의 예제와 같이 요청 헤더 조건에서도 동일하게 사용할 수 있다.
Java
@GetMapping(path = "/pets", headers = "myHeader=myValue") // (1)
public void findPet(@PathVariable String petId) {
// ...
}
Kotlin
@GetMapping("/pets", headers = ["myHeader=myValue"]) // (1)
fun findPet(@PathVariable petId: String) {
// ...
}
- (1)
myHeader
이myValue
와 같은지 확인해라.
HTTP HEAD, OPTIONS
@GetMapping
그리고 @RequestMapping(method=HttpMethod.GET)
는 요청 매핑 목적으로 HTTP HEAD를 투명하게 지원한다. 컨트롤러의 메서드를 변경할 필요가 없다. HttpHandler
서버 어댑터에 적용되는 응답 랩퍼는 Content-Length
헤더가 실제로 응답(body)에 기록하지 않고 기록 된 바이트 수로 설정되도록 한다.
기본적으로 HTTP OPTIONS는 Allow 응답 헤더를 일치하는 URL 패턴을 가진 모든 @RequestMapping
메서드에 나열되어 있는 HTTP 메서드 목록으로 설정하여 처리된다.
HTTP 메서드 선언이 없는 @RequestMapping
인 경우는 Allow
헤더는 GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS
로 설정된다. 컨트롤러 메서드는 지원되는 HTTP 메서드를 항상 선언해야 한다(예를 들어, HTTP 메서드 고유의 변형를 사용하는 등 - @GetMapping
, @PostMapping
).
@RequestMapping
메서드를 명시적으로 HTTP HEAD와 HTTP OPTIONS에 매핑 할 수 있지만 이는 일반적인 경우에는 필요 없다.
커스텀 어노테이션
Spring WebFlux는 요청 매핑에 컴포즈드 어노테이션 (composed annotations)의 사용을 지원한다. 이들은 그 자체가 @RequestMapping
메타 어노테이션 된 더 범위가 적고, 더 구체적인 목적에서 @RequestMapping
속성의 일부(또는 전부)를 다시 선언하도록 구성된 어노테이션이다.
@GetMapping
, @PostMapping
, @PutMapping
, @DeleteMapping
, @PatchMapping
는 구성된 어노테이션의 예이다. 아마도 대부분의 컨트롤러 메서드는 특정 HTTP 메서드에 매핑 할 필요가 있기 때문에, @RequestMapping
사용하는 것이 아니라 기본적으로 모든 HTTP 메서드와 일치하기 때문이다. 구성된 어노테이션의 예제가 필요한 경우, 그들이 어떻게 선언되어 있는지 확인해라.
Spring WebFlux는 사용자 요청 매칭 로직을 가진 사용자 요청 매핑 속성도 지원하고 있다. 이것은 RequestMappingHandlerMapping
하위 클래스와 getCustomMethodCondition
메서드를 재정의 필요로 하는 고급 옵션이다. 사용자 지정 특성을 확인하고 자체 RequestCondition
를 반환 할 수 있다.
명시적인 등록
핸들러 메서드를 프로그램에 등록 할 수 있다. 이것은 동적 등록 또는 다른 URL에서 동일한 핸들러가 다른 인스턴스와 같은 고급 케이스에 사용할 수 있다. 다음의 예는 그 방법을 보여준다.
Java
@Configuration
public class MyConfig {
@Autowired
public void setHandlerMapping(RequestMappingHandlerMapping mapping, UserHandler handler) // (1)
throws NoSuchMethodException {
RequestMappingInfo info = RequestMappingInfo
.paths("/user/{id}").methods(RequestMethod.GET).build(); // (2)
Method method = UserHandler.class.getMethod("getUser", Long.class); // (3)
mapping.registerMapping(info, handler, method); // (4)
}
}
Kotlin
@Configuration
class MyConfig {
@Autowired
fun setHandlerMapping(mapping: RequestMappingHandlerMapping, handler: UserHandler) { (1)
val info = RequestMappingInfo.paths("/user/{id}").methods(RequestMethod.GET).build() (2)
val method = UserHandler::class.java.getMethod("getUser", Long::class.java) (3)
mapping.registerMapping(info, handler, method) (4)
}
}
- (1) 대상 핸들러와 컨트롤러의 핸들러 매핑을 삽입한다.
- (2) 요청 매핑 메타 데이터를 준비한다.
- (3) 핸들러 메서드를 가져온다.
- (4) 등록을 추가한다.
1.4.3 처리 방법
@RequestMapping
핸들러 메서드는 유연한 시그너처가 지원되는 컨트롤러 메서드의 인수와 반환 값의 범위에서 선택할 수 있다.
메서드 인수
다음 표에는 지원되는 컨트롤러 메서드의 인수를 나타낸다.
리액티브 형 (Reactor, RxJava, 또는 기타)은 블로킹 I/O(요청 본문 읽기 등)를 해결해야 하는 인수로 지원하고 있다. 이것은 설명 열에 표시되어 있다. 차단을 필요로 하지 않는 인수는 리액티브 유형은 상정되어 있지 않다.
JDK 1.8의 java.util.Optional
는 required 속성 (예를 들어 @RequestParam
, @RequestHeader
등)을 가지는 어노테이션과 함께 메서드 인수로 지원되며, required=false
해당한다.
컨트롤러 메서드의 인수 | 설명 |
---|---|
ServerWebExchange |
전체 ServerWebExchange - HTTP 요청 및 응답 요청 및 세션 속성, checkNotModified 메서드 등의 컨테이너. |
ServerHttpRequest , ServerHttpResponse |
HTTP 요청 또는 응답에 액세스한다. |
WebSession |
세션에 액세스한다. 이것은 속성이 추가되지 않는 한 새로운 세션의 시작을 강제하지 않다. 리액티브 형을 지원한다. |
java.security.Principal |
현재 인증 된 사용자 - 기존의 경우, 특정 Principal 구현 클래스가 될 수 있다. 리액티브 형을 지원한다. |
org.springframework.http.HttpMethod |
요청의 HTTP 메서드 |
java.util.Locale |
사용 가능한 가장 구체적인 LocaleResolver 의해 결정되는 현재 요청 로케일 - 사실, 구성된 LocaleResolver /LocaleContextResolver . |
java.util.TimeZone + java.time.ZoneId |
LocaleContextResolver 에 의해 결정되는 현재의 요청에 관련된 시간대. |
@PathVariable |
URI 템플릿 변수에 액세스. URI 패턴을 참조해라. |
@MatrixVariable |
URI 경로 세그먼트의 이름과 값 쌍에 액세스. 행렬 변수를 참조해라. |
@RequestParam |
서블릿 요청 매개 변수에 액세스. 매개 변수 값은 선언 된 메서드 인자 타입으로 변환된다. @RequestParam 을 참조해라. @RequestParam 의 사용은 옵션 인 점에 유의해라. 예를 들어, 그 속성을 설정하는 것이다. 이 표 다음에 나오는 “추가 인수"를 참조해라. |
@RequestHeader |
요청 헤더에 액세스. 헤더 값은 선언 된 메서드 인자 타입으로 변환된다. @RequestHeader 을 참조해라. |
@CookieValue |
쿠키에 대한 액세스. Cookie 값은 선언 된 메서드 인자 타입으로 변환된다. @CookieValue 을 참조해라. |
@RequestBody |
HTTP 요청 본문에 액세스. 본문 내용은 HttpMessageReader 인스턴스를 사용하여 선언 된 메서드 인자 타입으로 변환된다. 리액티브 형을 지원한다. @RequestBody 을 참조해라. |
HttpEntity<B> |
요청 헤더와 본문에 액세스. 본체는 HttpMessageReader 인스턴스로 변환된다. 리액티브 형을 지원한다. HttpEntity 을 참조해라. |
@RequestPart |
multipart/form-data 요청의 일부에 액세스. 리액티브 형을 지원한다. 다중 콘텐츠 및 다중 데이터를 참조해라. |
java.util.Map, org.springframework.ui.Model , org.springframework.ui.ModelMap . |
HTML 컨트롤러에 사용되는 뷰 렌더링의 일부로 템플릿에 공개되는 모델에 액세스. |
@ModelAttribute |
데이터 바인딩과 검증이 적용된 모델의 기존 특성 (존재하지 않는 경우는 인스턴스화)에 액세스. @ModelAttribute 및 Model및 DataBinder를 참조해라. @ModelAttribute 의 사용은 옵션인 점에 유의해라. 예를 들어, 그 속성을 설정하는 것이다. 이 표 다음에 나오는 “추가 인수"를 참조해라. |
Errors , BindingResult |
명령 객체, 즉 @ModelAttribute 인수의 검증 및 데이터 바인딩에서 오류에 액세스. Errors 또는 BindingResult 인수는 검증 된 메서드 인수의 직후에 선언해야 한다. |
SessionStatus + 클래스 레벨 @SessionAttributes |
폼 처리의 완성을 기념하기 위해, 클래스 레벨의 @SessionAttributes 어노테이션을 통해 선언된 세션 속성 정리를 트리거한다. 자세한 내용은 @SessionAttributes 을 참조해라. |
UriComponentsBuilder |
현재 요청의 호스트, 포트, 스키마 컨텍스트 경로와 관련된 URL을 준비하기 위하여. URI 링크를 참조해라. |
@SessionAttribute |
모든 세션 속성에 대한 액세스 - 클래스 레벨의 @SessionAttributes 선언의 결과로 세션에 저장된 모델 속성과 대조. 자세한 내용은 @SessionAttribute 을 참조해라. |
@RequestAttribute |
요청 속성에 액세스. 자세한 내용은 @RequestAttribute 을 참조해라. |
기타 인수 | 메서드의 인수가 위의 어느 것에도 일치하지 않는 경우, 디폴트로 BeanUtils#isSimpleProperty 에 의해 결정되는 단순한 형태의 경우 @RequestParam 로, 그렇지 않은 경우 @ModelAttribute 로 해결된다. |
반환 값
다음 표에는 지원되는 컨트롤러 메서드의 반환 값을 보여준다. 이러한 Reactor, RxJava 등의 라이브러리에서의 반응 유형을 주의 또는 다른 일반적으로 모든 반환을 위해 지원하고 있다.
컨트롤러 메서드의 반환 값 | 설명 |
---|---|
@ResponseBody |
반환 값은 HttpMessageWriter 인스턴스를 통해 인코딩 된 응답에 기록된다. @ResponseBody 을 참조해라. |
HttpEntity<B> , ResponseEntity<B> |
반환 값은 HTTP 헤더를 포함한 전체 응답을 지정하고 본문 HttpMessageWriter 인스턴스를 통해 인코딩 된 응답에 기록된다. ResponseEntity 을 참조해라. |
HttpHeaders |
헤더를 포함하고, 본문이 포함되지 않은 응답을 반환하기. |
String |
ViewResolver 인스턴스에서 해결 된 암시 적 모델과 함께 사용되는 뷰 이름 - 명령 객체와 @ModelAttribute 메서드에 의해 결정된다. 핸들러 메서드는 Model 인수 ( 위 )를 선언함으로써 프로그램 모델을 강화 할 수 있다. |
View |
암시 적 모델과 함께 렌더링하는 데 사용하는 View 인스턴스 - 명령 객체와 @ModelAttribute 메서드에 의해 결정된다. 핸들러 메서드는 Model 인수 ( 위 )를 선언함으로써 프로그램 모델을 강화 할 수 있다. |
java.util.Map, org.springframework.ui.Model |
암시 적 모델에 추가되는 속성. 뷰 이름은 요청 경로에 따라 암묵적으로 결정된다. |
@ModelAttribute |
모델에 추가되는 속성. 뷰 이름은 요청 경로에 따라 암묵적으로 결정된다. @ModelAttribute 는 옵션이다. 이 표 다음에 나오는 “추가 반환"을 참조해라. |
Rendering |
모델과 뷰의 렌더링 시나리오에 대한 API. |
void |
void경우에 따라서는 비동기 ( Mono<Void> 등)의 반환 형식 (또는 null 반환 값)을 가지는 메서드는 ServerHttpResponse , ServerWebExchange 인수 또는 @ResponseStatus 어노테이션인 경우 응답을 완벽하게 처리 된 것으로 간주된다. 컨트롤러가 정의 ETag 또는 lastModified 타임 스탬프 확인을 한 경우도 마찬가지이다. // TODO : 자세한 내용은 컨트롤러를 참조해라. 위의 어느 것에도 들어 맞지 않는 경우 void 반환형은 REST 컨트롤러의 “응답 내용 없음"또는 HTML 컨트롤러의 기본 뷰 이름 선택을 나타낼 수도 있다. |
Flux<ServerSentEvent> , Observable<ServerSentEvent> 또는 기타 리액티브 형 |
서버 전송 이벤트를 발행한다. ServerSentEvent 래퍼는 데이터만을 기입 할 필요가 있는 경우는 생략 할 수 있다 (그러나 produces 속성을 사용하여 매핑 text/event-stream 을 요청하거나 선언해야 한다). |
다른 반환 값 | 반환 값이 위의 어느 것에도 일치하지 않는 경우, 기본적으로 뷰 이름으로 String 또는 void (기본 뷰 이름의 선택이 적용되는)로 또는 모델에 추가되는 모델 속성으로 처리된다. BeanUtils#isSimpleProperty 에서 결정된 간단 형이 아닌 경우는 미해결로 남아 있다. |
형식 변환
문자열 기반의 요청 입력을 나타내는 일부 어노테이션이 선언된 컨트롤러 메서드 인자(예 @RequestParam
, @RequestHeader
, @PathVariable
, @MatrixVariable
)는@CookieValue
인수가 String 아닌 것으로 선언 된 경우, 형식 변환을 필요로 하는 경우가 있다.
이런 경우 구성된 변환기를 기반으로 형식 변환이 자동으로 적용된다. 기본적으로 단순 형식 (int, long, Date등)을 지원하고 있다. 형식 변환 WebDataBinder
(DataBinder
참조) 또는 Formatters
를 FormattingConversionService
에 등록(Spring 필드의 형식 참조)하여 사용자 지정할 수 있다.
형식 변환의 실제적인 과제는 빈 문자열 소스 값의 처리이다. 이러한 값은 형식 변환의 결과로 null
이 경우 누락 된 것으로 간주된다. 이것은 Long, UUID 다른 대상 유형에 적용된다. null
삽입을 허용하는 경우는 인수의 어노테이션 required
플래그를 사용하거나 인수를 @Nullable
으로 선언한다.
행렬 변수
RFC 3986 경로 세그먼트의 이름과 값의 쌍에 대해 설명한다. Spring WebFlux는 Tim Berners-Lee에 따르면 “이전 게시물” 에 근거하는 “매트릭스 변수"라고 하지만, URI 경로 매개 변수라고도 한다.
매트릭스 변수는 임의의 경로 세그먼트에 표시 할 각 변수는 세미콜론으로 구분 된 여러 개의 값은 쉼표로 구분한다(예 : "/cars;color=red,green;year=2012"
). "color=red;color=green;color=blue"
와 같은 변수 이름을 반복 사용하여 여러 값을 지정할 수도 있다.
Spring MVC와 달리 WebFlux는 URL의 매트릭스 변수의 유무는 요청 매핑에 영향을 주지 않다. 즉, URI 컨텐츠를 사용하여 변수의 내용을 마스크 할 필요가 없다. 즉, 컨트롤러 메서드에서 매트릭스 변수에 액세스하려면 매트릭스 변수가 예상되는 경로 세그먼트에 URI 변수를 추가해야 한다. 다음의 예제는 그 방법을 보여준다.
Java
// GET /pets/42;q=11;r=22
@GetMapping("/pets/{petId}")
public void findPet(@PathVariable String petId, @MatrixVariable int q) {
// petId == 42
// q == 11
}
Kotlin
// GET /pets/42;q=11;r=22
@GetMapping("/pets/{petId}")
fun findPet(@PathVariable petId: String, @MatrixVariable q: Int) {
// petId == 42
// q == 11
}
모든 경로 세그먼트 매트릭스 변수를 포함 할 수 있기 때문에 다음의 예와 같이, 행렬 변수가 어떤 경로 변수에있을 것으로 예상되는지를 명확하게 할 필요가 있을 수 있다.
Java
// GET /owners/42;q=11/pets/21;q=22
@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
@MatrixVariable(name="q", pathVar="ownerId") int q1,
@MatrixVariable(name="q", pathVar="petId") int q2) {
// q1 == 11
// q2 == 22
}
Kotlin
@GetMapping("/owners/{ownerId}/pets/{petId}")
fun findPet(
@MatrixVariable(name = "q", pathVar = "ownerId") q1: Int,
@MatrixVariable(name = "q", pathVar = "petId") q2: Int) {
// q1 == 11
// q2 == 22
}
다음의 예와 같이, 행렬 변수를 옵션으로 정의하고 기본값을 지정할 수 있다.
Java
// GET /pets/42
@GetMapping("/pets/{petId}")
public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) {
// q == 1
}
Kotlin
// GET /pets/42
@GetMapping("/pets/{petId}")
fun findPet(@MatrixVariable(required = false, defaultValue = "1") q: Int) {
// q == 1
}
모든 행렬 변수를 얻으려면 다음 예제와 같이 MultiValueMap
사용한다.
Java
// GET /owners/42;q=11;r=12/pets/21;q=22;s=23
@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
@MatrixVariable MultiValueMap<String, String> matrixVars,
@MatrixVariable(pathVar="petId") MultiValueMap<String, String> petMatrixVars) {
// matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
// petMatrixVars: ["q" : 22, "s" : 23]
}
Kotlin
// GET /owners/42;q=11;r=12/pets/21;q=22;s=23
@GetMapping("/owners/{ownerId}/pets/{petId}")
fun findPet(
@MatrixVariable matrixVars: MultiValueMap<String, String>,
@MatrixVariable(pathVar="petId") petMatrixVars: MultiValueMap<String, String>) {
// matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
// petMatrixVars: ["q" : 22, "s" : 23]
}
@RequestParam
@RequestParam
어노테이션을 사용하여 쿼리 매개 변수를 컨트롤러 메서드 인수에 바인딩 할 수 있다. 다음 코드에서 사용 방법을 보여준다.
Java
@Controller
@RequestMapping("/pets")
public class EditPetForm {
// ...
@GetMapping
public String setupForm(@RequestParam("petId") int petId, Model model) { // (1)
Pet pet = this.clinic.loadPet(petId);
model.addAttribute("pet", pet);
return "petForm";
}
// ...
}
Kotlin
import org.springframework.ui.set
@Controller
@RequestMapping("/pets")
class EditPetForm {
// ...
@GetMapping
fun setupForm(@RequestParam("petId") petId: Int, model: Model): String { // (1)
val pet = clinic.loadPet(petId)
model["pet"] = pet
return "petForm"
}
// ...
}
- (1)
@RequestParam
를 사용한다.
서블릿 API의 “요청 매개 변수"의 개념은 쿼리 매개 변수 양식 데이터, 멀티 파트를 하나로 통합한다. 그러나 WebFlux는 각각
ServerWebExchange
을 통해 개별적으로 액세스한다.@RequestParam
쿼리 매개 변수 만 바인딩하지만 데이터 바인딩을 사용하여 쿼리 매개 변수 양식 데이터, 멀티 파트를 명령 개체 에 적용 할 수 있다.
@RequestParam
어노테이션을 사용하여 메서드 매개 변수는 기본적으로 필요하지만 @RequestParam
필수 플래그를 false 설정하거나 java.util.Optiona
l 래퍼 인수를 선언하여 메서드 매개 변수가 선택임을 지정할 수 있다.
대상 메서드의 매개 변수 타입이 String
아니다 경우 유형 변환이 자동으로 적용된다. 형식 변환을 참조해라.
@RequestParam
어노테이션 Map<String, String>
또는 MultiValueMap<String, String>
인수로 선언되면 맵에 모든 쿼리 매개 변수가 입력된다.
@RequestParam
의 사용은 옵션인 점에 유의해라. 예를 들어, 그 속성을 설정하는 것이다. 디폴트로 (BeanUtils#isSimpleProperty
에 의해 결정) 간단한 값 형식이고 다른 인수 확인자에 의해 해결되지 않는 인수 @RequestParam
에 어노테이션 을 붙일 수 있는 것처럼 처리된다.
@RequestHeader
@RequestHeader
어노테이션을 사용하여 요청 헤더를 컨트롤러 메서드 인수에 바인딩 할 수 있다.
다음의 예는 헤더와 함께 요청을 보여준다.
Host localhost : 8080
Accept text / html application / xhtml + xml application / xml; q = 0.9
Accept-Language fr en-gb; q = 0.7, en; q = 0.3
Accept-Encoding gzip, deflate
Accept-Charset ISO-8859-1, utf-8; q = 0.7 *; q = 0.7
Keep-Alive 300
다음의 예제는 Accept-Encoding
및 Keep-Alive
헤더의 값을 가져온다.
Java
@GetMapping("/demo")
public void handle(
@RequestHeader("Accept-Encoding") String encoding,
@RequestHeader("Keep-Alive") long keepAlive) {
//...
}
Kotlin
@GetMapping("/demo")
fun handle(
@RequestHeader("Accept-Encoding") encoding: String,
@RequestHeader("Keep-Alive") keepAlive: Long) {
//...
}
Accept-Encoging
헤더의 값을 가져온다.
Keep-Alive
헤더의 값을 가져온다.
대상 메서드의 매개 변수 타입이 String아니다 경우 유형 변환이 자동으로 적용된다. 형식 변환을 참조해라.
@RequestHeader
어노테이션이 Map<String, String>
, MultiValueMap<String, String>
또는 HttpHeaders
인수로 사용되는 경우 맵에는 모든 헤더 값이 입력된다.
쉼표로 구분 된 문자열을 문자열 배열이나 컬렉션 또는 형식 변환 시스템에 알려진 다른 형식으로 변환하기위한 내장 지원이 가능하다. 예 : @RequestHeader("Accept")
어노테이션이 선언된 메서드 매개 변수는 타입 String 인 경우가 있다만, String[]
또는 List<String>
일 수 있다.
@CookieValue
@CookieValue
어노테이션을 사용하여 HTTP Cookie의 값을 컨트롤러의 메서드 인수에 바인딩 할 수 있다.
다음 예제는 Cookie
를 사용하여 요청을 보여준다.
JSESSIONID = 415A4AC178C59DACE0B2C9CA727CDD84
다음 코드 예제는 Cookie 값을 검색하는 방법을 보여준다.
Java
@GetMapping("/demo")
public void handle(@CookieValue("JSESSIONID") String cookie) { // (1)
//...
}
Kotlin
@GetMapping("/demo")
fun handle(@CookieValue("JSESSIONID") cookie: String) { // (1)
//...
}
- (1) Cookie 값을 가져온다.
대상 메서드의 매개 변수 타입이 String
아닌 경우 유형 변환이 자동으로 적용된다. 형식 변환을 참조해라.
@ModelAttribute
메서드의 인수로 @ModelAttribute
어노테이션을 사용하여 모델의 속성에 액세스하거나 존재하지 않는 경우는 인스턴스화 할 수 있다. 또한 모델 속성은 필드 이름과 일치하는 이름을 가진 쿼리 매개 변수와 양식 필드의 값에 오버레이된다. 이것은 데이터 바인딩이라고 개별 쿼리 매개 변수와 양식 필드의 해석과 변환을 다룰 필요가 없다. 다음 예제에서는 Pet 인스턴스를 바인딩한다.
Java
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute Pet pet) { } // (1)
Kotlin
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute pet: Pet): String { } // (1)
- (1) Pet 인스턴스를 바인딩한다.
위의 예제의 Pet 인스턴스는 다음과 같이 해결된다.
Model
를 통해 이미 추가되어있는 경우 모델에서.- HTTP 세션에서
@SessionAttributes
를 통해. - 기본 생성자의 호출.
- 쿼리 매개 변수 또는 양식 필드에 일치하는 인수를 가지는 “기본 생성자"의 호출에서 인수 이름은 JavaBeans
@ConstructorProperties
또는 바이트 코드의 실행시 보유 매개 변수 이름에 의해 결정된다.
모델 속성 인스턴스가 취득된 후 데이터 바인딩이 적용된다. WebExchangeDataBinder
클래스는 쿼리 매개 변수와 양식 필드의 이름을 대상 Object
필드 이름과 일치시킨다. 일치하는 필드는 필요에 따라 형태 변환이 적용된 후 입력된다. 데이터 바인딩(및 확인)에 대한 자세한 내용은 확인 을 참조해라. 데이터 바인딩의 정의에 대한 자세한 내용은 DataBinder
을 참조해라.
데이터 바인딩 오류가 발생할 수 있다. 기본적으로 WebExchangeBindException
이 발생하지만, 컨트롤러 메서드에서 이러한 오류를 확인하려면 다음 예제와 같이 @ModelAttribute
바로 옆에 BindingResult
인수를 추가 할 수 있다.
Java
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) { // (1)
if (result.hasErrors()) {
return "petForm";
}
// ...
}
Kotlin
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute("pet") pet: Pet, result: BindingResult): String { // (1)
if (result.hasErrors()) {
return "petForm"
}
// ...
}
- (1)
BindingResult
를 추가한다.
javax.validation.Valid
어노테이션 또는 Spring의 @Validated
어노테이션을 추가하여 데이터 바인딩 후 확인을 자동으로 적용 할 수 있다(Bean 검증과 Spring 검증 참조). 다음 예제에서는@Valid
어노테이션을 사용하고 있다.
Java
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) { (1)
if (result.hasErrors()) {
return "petForm";
}
// ...
}
Kotlin
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@Valid @ModelAttribute("pet") pet: Pet, result: BindingResult): String { // (1)
if (result.hasErrors()) {
return "petForm"
}
// ...
}
- (1) 모델 속성 인수
@Valid
를 사용한다.
Spring WebFlux은 Spring MVC와 달리 모델의 리액티브 형을 지원한다. 예를 들어, Mono<Account>
또는 io.reactivex.Single<Account>
이다. @ModelAttribute
인수는 리액티브 형 래퍼의 유무에 관계없이 선언 할 수 있다. 필요에 따라 실제 값에 따라 해결된다. 그러나 BindingResult 인수를 사용하려면 전술 한 바와 같이, 리액티브 형 래퍼없이 @ModelAttribute
인수를 선언 할 필요가 있다는 점에 유의해라. 또는 다음의 예와 같이, 리액티브 형을 사용하여 오류를 처리 할 수 있다.
Java
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public Mono<String> processSubmit(@Valid @ModelAttribute("pet") Mono<Pet> petMono) {
return petMono
.flatMap(pet -> {
// ...
})
.onErrorResume(ex -> {
// ...
});
}
Kotlin
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@Valid @ModelAttribute("pet") petMono: Mono<Pet>): Mono<String> {
return petMono
.flatMap { pet ->
// ...
}
.onErrorResume{ ex ->
// ...
}
}
@ModelAttribute
의 사용은 옵션인 점에 유의해라. 예를 들어, 그 속성을 설정하는 것이다. 기본적으로 간단한 값 타입( BeanUtils#isSimpleProperty
에 의해 결정) 대신 다른 인수 확인자에 의해 해결되지 않는 인수 @ModelAttribute
에 어노테이션이 추가 된 것으로 간주된다.
@SessionAttributes
@SessionAttributes
는 요청 사이에서 WebSession
모델 속성을 저장하는데 사용된다. 이것은 특정 컨트롤러가 사용하는 세션 속성을 선언하는 형식 수준의 어노테이션이다. 이것은 일반적으로 후속 액세스 요청을 위해 세션에 투명하게 저장되는 모델 속성의 이름 또는 모델 속성의 유형을 나열한다.
다음의 예를 생각해 봅시다.
Java
@Controller
@SessionAttributes("pet")
public class EditPetForm {
// ...
}
Kotlin
@Controller
@SessionAttributes("pet")
class EditPetForm {
// ...
}
@SessionAttributes
어노테이션을 사용한다.
첫 번째 요청에서 이름이 pet모델 속성이 모델에 추가되면 자동으로 WebSession
승격 저장된다. 다음의 예와 같이 다른 컨트롤러 메서드가 SessionStatus
메서드 인수를 사용하여 스토리지를 지울 때까지 거기에 남아 있다.
Java
@Controller
@SessionAttributes("pet") // (1)
public class EditPetForm {
// ...
@PostMapping("/pets/{id}")
public String handle(Pet pet, BindingResult errors, SessionStatus status) { // (2)
if (errors.hasErrors()) {
// ...
}
status.setComplete();
// ...
}
}
}
Kotlin
@Controller
@SessionAttributes("pet") // (1)
class EditPetForm {
// ...
@PostMapping("/pets/{id}")
fun handle(pet: Pet, errors: BindingResult, status: SessionStatus): String { // (2)
if (errors.hasErrors()) {
// ...
}
status.setComplete()
// ...
}
}
- (1)
@SessionAttributes
어노테이션을 사용한다. - (2)
SessionStatus
변수를 사용한다.
@SessionAttribute
글로벌 (즉, 필터에 의해 컨트롤러 외부에서) 관리되는 기존의 세션 속성에 액세스해야 존재하는 것과 존재하지 않는 경우, 메서드 매개 변수에 @SessionAttribute
어노테이션을 사용할 수 있다. 다음의 예를 보여준다.
Java
@GetMapping("/")
public String handle(@SessionAttribute User user) { // (1)
// ...
}
Kotlin
@GetMapping("/")
fun handle(@SessionAttribute user: User): String { // (1)
// ...
}
- (1)
@SessionAttribute
를 사용한다. 세션 속성을 추가하거나 제거 할 필요가있다 유스 케이스의 경우WebSession
를 컨트롤러 메서드에 삽입하는 것을 고려해라.
컨트롤러 워크 플로우의 일부로 세션에서 모델 속성을 일시적으로 저장하려면 @SessionAttributes
따라 SessionAttributes
사용을 고려해라.
@RequestAttribute
@SessionAttribute
뿐만 아니라 다음의 예와 같이, @RequestAttribute
어노테이션을 사용하여 이전에 작성된 기존의 요청 속성에 액세스 할 수 있다(예 WebFilter
통해).
Java
@GetMapping("/")
public String handle(@RequestAttribute Client client) { // (1)
// ...
}
Kotlin
@GetMapping("/")
fun handle(@RequestAttribute client: Client): String { // (1)
// ...
}
- (1)
@RequestAttribute
를 사용한다.
다중 콘텐츠
다중 데이터 에서 설명한 바와 같이, ServerWebExchange
멀티 파트 콘텐츠에 대한 액세스를 제공한다. 다음의 예와 같이 컨트롤러에서 파일 업로드 폼 (브라우저 등)을 처리하는 가장 좋은 방법은 명령 객체 에 데이터 바인딩을 사용하는 것이다.
Java
class MyForm {
private String name;
private MultipartFile file;
// ...
}
@Controller
public class FileUploadController {
@PostMapping("/form")
public String handleFormUpload(MyForm form, BindingResult errors) {
// ...
}
}
Kotlin
class MyForm(
val name: String,
val file: MultipartFile)
@Controller
class FileUploadController {
@PostMapping("/form")
fun handleFormUpload(form: MyForm, errors: BindingResult): String {
// ...
}
}
RESTful 서비스 시나리오에서 브라우저 이외의 클라이언트에서 멀티 파트 요청을 보낼 수 있다. 다음 예제에서는 JSON과 함께 파일을 사용하고 있다.
POST / someUrl
Content-Type : multipart / mixed
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
Content-Disposition : form-data; name = "meta-data"
Content-Type : application / json; charset = UTF-8
Content-Transfer-Encoding : 8bit
{
"name": "value"
}
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
Content-Disposition : form-data; name = "file-data"; filename = "file.properties"
Content-Type : text / xml
Content-Transfer-Encoding : 8bit
... File Data ...
다음의 예와 같이, @RequestPart
을 사용하여 개별 부품에 액세스 할 수 있다.
Java
@PostMapping("/")
public String handle(@RequestPart("meta-data") Part metadata, // (1)
@RequestPart("file-data") FilePart file) { // (2)
// ...
}
Kotlin
@PostMapping("/")
fun handle(@RequestPart("meta-data") Part metadata, // (1)
@RequestPart("file-data") FilePart file): String { // (2)
// ...
}
- (1)
@RequestPart
를 사용하여 메타 데이터를 가져옵니다. - (2)
@RequestPart
을 사용하여 파일을 가져옵니다.
다음의 예와 같이, 익지 않는 부분의 컨텐츠를 직렬화하는 (예를 들어, @RequestBody
비슷한 JSON으로) 위해 Part
대신 구체적인 대상 Object
을 선언 할 수 있다.
Java
@PostMapping("/")
public String handle(@RequestPart("meta-data") MetaData metadata) { // (1)
// ...
}
Kotlin
@PostMapping("/")
fun handle(@RequestPart("meta-data") metadata: MetaData): String { // (1)
// ...
}
- (1)
@RequestPart
를 사용하여 메타 데이터를 가져옵니다.
@RequestPart
를 javax.validation.Valid
또는 Spring의 @Validated
어노테이션과 함께 사용할 수 있다. 따라서 표준 Bean 검증이 적용된다. 검증 오류로 인해 WebExchangeBindException
발생하고 400 (BAD_REQUEST) 응답이 발생한다. 예외는 오류 정보를 포함한 BindingResult
이 포함된다. 또한 비동기 래퍼 인수를 선언하고 오류 관련 연산자를 사용하여 컨트롤러 메서드에서 처리 할 수 있다.
Java
@PostMapping("/")
public String handle(@Valid @RequestPart("meta-data") Mono<MetaData> metadata) {
// use one of the onError* operators...
}
Kotlin
@PostMapping("/")
fun handle(@Valid @RequestPart("meta-data") metadata: MetaData): String {
// ...
}
모든 다중 데이터 MultiValueMap
로 액세스하려면 다음 예제와 같이 @RequestBody
사용할 수 있다.
Java
@PostMapping("/")
public String handle(@RequestBody Mono<MultiValueMap<String, Part>> parts) { // (1)
// ...
}
Kotlin
@PostMapping("/")
fun handle(@RequestBody parts: MultiValueMap<String, Part>): String { // (1)
// ...
}
- (1)
@RequestBody
를 사용한다.
다음의 예와 같이 스트리밍 형식으로 다중 데이터에 순차적으로 액세스하려면 대신 @RequestBody
과 Flux<Part>
(또는 Kotlin의 Flow<Part>
)를 사용할 수 있다.
Java
@PostMapping("/")
public String handle(@RequestBody Flux<Part> parts) { // (1)
// ...
}
Kotlin
@PostMapping("/")
fun handle(@RequestBody parts: Flow<Part>): String { // (1)
// ...
}
- (1)
@RequestBody
를 사용한다.
@RequestBody
@RequestBody
어노테이션을 사용하여 요청 내용을 읽고 HttpMessageReader
을 통해 Object
비 일련 화 할 수 있다. 다음 예제에서는 @RequestBody
인수를 사용하고 있다.
Java
@PostMapping("/accounts")
public void handle(@RequestBody Account account) {
// ...
}
Kotlin
@PostMapping("/accounts")
fun handle(@RequestBody account: Account) {
// ...
}
Spring MVC와 달리 WebFlux는 @RequestBody
메서드의 인수는 리액티브 형 완전한 비 차단 읽기 및 (클라이언트에서 서버로의) 스트리밍을 지원한다.
Java
@PostMapping("/accounts")
public void handle(@RequestBody Mono<Account> account) {
// ...
}
Kotlin
@PostMapping("/accounts")
fun handle(@RequestBody accounts: Flow<Account>) {
// ...
}
WebFlux 구성 의 HTTP 메시지 코덱 옵션을 사용하여 메시지 리더를 구성 또는 사용자 정의 할 수 있다.
@RequestBody
를 javax.validation.Valid
또는 Spring
의 @Validated
어노테이션과 함께 사용할 수 있다. 따라서 표준 Bean 검증이 적용된다. 검증 오류로 인해 WebExchangeBindException발생하고 400 (BAD_REQUEST) 응답이 발생한다. 예외는 오류 정보를 포함한 BindingResult 이 포함되어 비동기 래퍼 인수를 선언하고 오류 관련 연산자를 사용하여 컨트롤러 메서드에서 처리 할 수 있다.
Java
@PostMapping("/accounts")
public void handle(@Valid @RequestBody Mono<Account> account) {
// use one of the onError* operators...
}
Kotlin
@PostMapping("/accounts")
fun handle(@Valid @RequestBody account: Mono<Account>) {
// ...
}
HttpEntity
HttpEntity
은 @RequestBody
을 사용하는 경우와 거의 동일하지만 요청 헤더와 본문을 공개하는 컨테이너 개체에 따라 달라진다. 다음 예제에서는 HttpEntity
사용하고 있다.
Java
@PostMapping("/accounts")
public void handle(HttpEntity<Account> entity) {
// ...
}
Kotlin
@PostMapping("/accounts")
fun handle(entity: HttpEntity<Account>) {
// ...
}
@ResponseBody
메서드 @ResponseBody
어노테이션을 사용하여 HttpMessageWriter
를 통해 반환 값을 응답 본체에 직렬화 할 수 있다. 다음의 예는 그 방법을 보여준다.
Java
@GetMapping("/accounts/{id}")
@ResponseBody
public Account handle() {
// ...
}
Kotlin
@GetMapping("/accounts/{id}")
@ResponseBody
fun handle(): Account {
// ...
}
@ResponseBody
클래스 수준에서도 지원된다. 이 경우 모든 컨트롤러 메서드에 의해 상속된다. 이것은 @RestController
효과이며, 이것은 @Controller
및 @ResponseBody
으로 표시된 메타 어노테이션에 지나지 않는다.
@ResponseBody
는 리액티브 형을 지원한다. 즉, Reactor 형 또는 RxJava 형을 돌려 그들이 생성하는 비동기 값을 응답으로 렌더링 할 수 있다. 자세한 내용은 스트리밍 및 JSON 렌더링을 참조해라.
@ResponseBody
메서드를 JSON 직렬화 뷰와 결합 할 수 있다. 자세한 내용은 Jackson JSON 을 참조해라.
WebFlux 구성 의 HTTP 메시지 코덱 옵션을 사용하여 메시지 쓰기 설정 또는 사용자 정의 할 수 있다.
ResponseEntity
ResponseEntity
는 @ResponseBody
와 비슷하지만, 상태 및 헤더가 있다. 예 :
Java
@GetMapping("/something")
public ResponseEntity<String> handle() {
String body = ... ;
String etag = ... ;
return ResponseEntity.ok().eTag(etag).build(body);
}
Kotlin
@GetMapping("/something")
fun handle(): ResponseEntity<String> {
val body: String = ...
val etag: String = ...
return ResponseEntity.ok().eTag(etag).build(body)
}
WebFlux은 단일 값의 리액티브 형 을 사용하여 ResponseEntity
비동기 적으로 생성 할 수 및 / 또는 몸의 단일 값 및 다중 값 리액티브 형을 사용하는 것을 지원한다. 그러면 다음과 같이 ResponseEntity
하고 다양한 비동기 응답이 가능하다.
ResponseEntity<Mono<T>>
또는 ResponseEntity<Flux<T>>
이 응답 상태와 헤더를 즉시 통지하지만 본문은 후 시점에서 비동기적으로 제공된다. 본체가 0..1 값으로 구성되어있는 경우 Mono
를 사용하여 여러 값을 생성 할 경우 Flux
사용한다.
Mono<ResponseEntity<T>>
이 세 가지를 모두 제공 - 후 시점에서 비동기 적으로 응답 상태, 머리글, 본문. 따라서 비동기 요청 처리 결과에 따라 응답의 상태와 헤더를 바꿀 수 있다.
Mono<ResponseEntity<Mono<T>>>
또는 Mono<ResponseEntity<Flux<T>>
는 일반적이지는 않지만, 또 다른 가능성이 있다. 이들은 먼저 응답 상태와 헤더를 비동기적으로 제공한 후 응답 본체를 비동기적으로 제공한다.
Jackson JSON
Spring은 Jackson JSON 라이브러리 지원을 제공한다.
JSON보기
Spring WebFlux는 Object
모든 필드의 일부만을 렌더링 할 수 Jackson의 직렬화보기 내장 지원을 제공한다. @ResponseBody
또는 ResponseEntity 컨트롤러 메서드에서 사용하려면 다음 예제와 같이 Jackson의 @JsonView
어노테이션을 사용하여 직렬화 뷰 클래스를 활성화한다.
Java
@RestController
public class UserController {
@GetMapping("/user")
@JsonView(User.WithoutPasswordView.class)
public User getUser() {
return new User("eric", "7!jd#h23");
}
}
public class User {
public interface WithoutPasswordView {};
public interface WithPasswordView extends WithoutPasswordView {};
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
@JsonView(WithoutPasswordView.class)
public String getUsername() {
return this.username;
}
@JsonView(WithPasswordView.class)
public String getPassword() {
return this.password;
}
}
Kotlin
@RestController
class UserController {
@GetMapping("/user")
@JsonView(User.WithoutPasswordView::class)
fun getUser(): User {
return User("eric", "7!jd#h23")
}
}
class User(
@JsonView(WithoutPasswordView::class) val username: String,
@JsonView(WithPasswordView::class) val password: String
) {
interface WithoutPasswordView
interface WithPasswordView : WithoutPasswordView
}
@JsonView
는 뷰 클래스의 배열을 사용할 수 있지만 지정할 수 컨트롤러 메서드마다 1개뿐이다. 여러 뷰를 활성화 할 필요가 있는 경우에는 복합 인터페이스를 사용한다.