Spring Web Reactive | 1. Spring WebFlux | 1.4. Annotated Controllers #1

Web MVC

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

Web MVC

표준 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)

Web MVC

@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 패턴

Web MVC

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)

Web MVC

여러 패턴이 URL과 일치하는 경우, 비교하여 최적의 일치를 찾아야 한다. 이것은 보다 더 구체적인 패턴을 찾는 PathPattern.SPECIFICITY_COMPARATOR으로 이루어진다.

모든 패턴에 대한 URI 변수와 와일드 카드의 숫자에 따라 점수가 계산된다. URI 변수의 점수는 와일드 카드보다 낮다. 총 점수가 낮은 패턴이 우선된다. 2개의 패턴의 점수가 동일한 경우, 긴 쪽이 선택된다.

포괄 패턴 (예를 들어 **, {*varName})는 채점에서 제외되고 대신 항상 마지막으로 정렬된다. 두 패턴이 모두 포괄인 경우, 긴 쪽이 선택된다.

소비 가능한 미디어 유형(Consumable Media Types)

Web MVC

다음의 예제와 같이 요청의 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/plaintext/plain이외의 콘텐츠 형식을 의미한다.

클래스 레벨에서 consumes 속성을 선언하여 클래스 전체에서 공유할 수도 있다. 단, 다른 대부분의 요청 매핑 속성과 달리 클래스 레벨에서 사용하는 경우, 메서드 레벨의 consumes 속성은 클래스 레벨의 선언을 계승하는 것이 아니라 우선시 한다.

MediaTypeAPPLICATION_JSON_VALUE, APPLICATION_XML_VALUE 등의 일반적으로 사용되는 미디어 유형에 상수를 제공한다.

생산 가능한 미디어 유형(Producible Media Types)

Web MVC

다음의 예와 같이, 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/plaintext/plain이외의 콘텐츠 형식을 의미한다.

클래스 레벨에서 produces 속성을 선언하여 클래스 전체에서 공유할 수도 있다. 단, 다른 대부분의 요청 매핑 속성과 달리 클래스 레벨에서 사용하는 경우, 메서드 레벨의 produces 속성은 클래스 레벨의 선언을 계승하는 것이 아니라 우선시 한다.

MediaType 일반적으로 사용되는 미디어 유형에 상수를 제공한다. APPLICATION_JSON_VALUE, APPLICATION_XML_VALUE

매개 변수와 헤더

Web MVC

쿼리 매개 변수의 조건에 따라 요청 매핑되는 범위를 줄일 수 있다. 쿼리 매개 변수의 존재하거나(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) myParammyValue와 같은지 확인해라.

다음의 예제와 같이 요청 헤더 조건에서도 동일하게 사용할 수 있다.

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) myHeadermyValue와 같은지 확인해라.

HTTP HEAD, OPTIONS

Web MVC

@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에 매핑 할 수 있지만 이는 일반적인 경우에는 필요 없다.

커스텀 어노테이션

Web MVC

Spring WebFlux는 요청 매핑에 컴포즈드 어노테이션 (composed annotations)의 사용을 지원한다. 이들은 그 자체가 @RequestMapping 메타 어노테이션 된 더 범위가 적고, 더 구체적인 목적에서 @RequestMapping 속성의 일부(또는 전부)를 다시 선언하도록 구성된 어노테이션이다.

@GetMapping, @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping 는 구성된 어노테이션의 예이다. 아마도 대부분의 컨트롤러 메서드는 특정 HTTP 메서드에 매핑 할 필요가 있기 때문에, @RequestMapping 사용하는 것이 아니라 기본적으로 모든 HTTP 메서드와 일치하기 때문이다. 구성된 어노테이션의 예제가 필요한 경우, 그들이 어떻게 선언되어 있는지 확인해라.

Spring WebFlux는 사용자 요청 매칭 로직을 가진 사용자 요청 매핑 속성도 지원하고 있다. 이것은 RequestMappingHandlerMapping 하위 클래스와 getCustomMethodCondition 메서드를 재정의 필요로 하는 고급 옵션이다. 사용자 지정 특성을 확인하고 자체 RequestCondition를 반환 할 수 있다.

명시적인 등록

Web MVC

핸들러 메서드를 프로그램에 등록 할 수 있다. 이것은 동적 등록 또는 다른 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 처리 방법

Web MVC

@RequestMapping 핸들러 메서드는 유연한 시그너처가 지원되는 컨트롤러 메서드의 인수와 반환 값의 범위에서 선택할 수 있다.

메서드 인수

Web MVC

다음 표에는 지원되는 컨트롤러 메서드의 인수를 나타낸다.

리액티브 형 (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로 해결된다.

반환 값

Web MVC

다음 표에는 지원되는 컨트롤러 메서드의 반환 값을 보여준다. 이러한 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에서 결정된 간단 형이 아닌 경우는 미해결로 남아 있다.

형식 변환

Web MVC

문자열 기반의 요청 입력을 나타내는 일부 어노테이션이 선언된 컨트롤러 메서드 인자(예 @RequestParam, @RequestHeader, @PathVariable, @MatrixVariable)는@CookieValue 인수가 String 아닌 것으로 선언 된 경우, 형식 변환을 필요로 하는 경우가 있다.

이런 경우 구성된 변환기를 기반으로 형식 변환이 자동으로 적용된다. 기본적으로 단순 형식 (int, long, Date등)을 지원하고 있다. 형식 변환 WebDataBinder(DataBinder참조) 또는 FormattersFormattingConversionService에 등록(Spring 필드의 형식 참조)하여 사용자 지정할 수 있다.

형식 변환의 실제적인 과제는 빈 문자열 소스 값의 처리이다. 이러한 값은 형식 변환의 결과로 null 이 경우 누락 된 것으로 간주된다. 이것은 Long, UUID 다른 대상 유형에 적용된다. null 삽입을 허용하는 경우는 인수의 어노테이션 required 플래그를 사용하거나 인수를 @Nullable으로 선언한다.

행렬 변수

Web MVC

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

Web MVC

@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.Optional 래퍼 인수를 선언하여 메서드 매개 변수가 선택임을 지정할 수 있다.

대상 메서드의 매개 변수 타입이 String아니다 경우 유형 변환이 자동으로 적용된다. 형식 변환을 참조해라.

@RequestParam 어노테이션 Map<String, String> 또는 MultiValueMap<String, String> 인수로 선언되면 맵에 모든 쿼리 매개 변수가 입력된다.

@RequestParam 의 사용은 옵션인 점에 유의해라. 예를 들어, 그 속성을 설정하는 것이다. 디폴트로 (BeanUtils#isSimpleProperty에 의해 결정) 간단한 값 형식이고 다른 인수 확인자에 의해 해결되지 않는 인수 @RequestParam에 어노테이션 을 붙일 수 있는 것처럼 처리된다.

@RequestHeader

Web MVC

@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-EncodingKeep-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

Web MVC

@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

Web MVC

메서드의 인수로 @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

Web MVC

@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

Web MVC

글로벌 (즉, 필터에 의해 컨트롤러 외부에서) 관리되는 기존의 세션 속성에 액세스해야 존재하는 것과 존재하지 않는 경우, 메서드 매개 변수에 @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

Web MVC

@SessionAttribute뿐만 아니라 다음의 예와 같이, @RequestAttribute 어노테이션을 사용하여 이전에 작성된 기존의 요청 속성에 액세스 할 수 있다(예 WebFilter통해).

Java

@GetMapping("/")
public String handle(@RequestAttribute Client client) {  // (1)
    // ...
}

Kotlin

@GetMapping("/")
fun handle(@RequestAttribute client: Client): String {  // (1)
    // ...
}
  • (1) @RequestAttribute를 사용한다.

다중 콘텐츠

Web MVC

다중 데이터 에서 설명한 바와 같이, 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를 사용하여 메타 데이터를 가져옵니다.

@RequestPartjavax.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를 사용한다.

다음의 예와 같이 스트리밍 형식으로 다중 데이터에 순차적으로 액세스하려면 대신 @RequestBodyFlux<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

Web MVC

@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

Web MVC

HttpEntity@RequestBody을 사용하는 경우와 거의 동일하지만 요청 헤더와 본문을 공개하는 컨테이너 개체에 따라 달라진다. 다음 예제에서는 HttpEntity사용하고 있다.

Java

@PostMapping("/accounts")
public void handle(HttpEntity<Account> entity) {
    // ...
}

Kotlin

@PostMapping("/accounts")
fun handle(entity: HttpEntity<Account>) {
    // ...
}

@ResponseBody

Web MVC

메서드 @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

Web MVC

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보기

Web MVC

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개뿐이다. 여러 뷰를 활성화 할 필요가 있는 경우에는 복합 인터페이스를 사용한다.




최종 수정 : 2021-04-12