Spring Web Reactive | 1. Spring WebFlux | 1.4. Annotated Controllers #2
1.4.4. Model
@ModelAttribute
어노테이션을 사용할 수 있다.
모델에서 개체를 만들거나 액세스하고 WebDataBinder
를 통해 요청에 바인딩 @RequestMapping
메서드의 메서드 인수 .
@Controller
또는 @ControllerAdvice
클래스의 메서드 레벨의 어노테이션으로 @RequestMapping
메서드를 호출하기 전에 모델을 초기화 할 수 있다.
@RequestMapping
메서드에서 반환 값을 모델 속성으로 표시한다.
이 섹션에서는 @ModelAttribute
메서드 또는 이전 목록의 두 번째 항목에 대해 설명한다. 컨트롤러는 @ModelAttribute
방법을 얼마든지 가질 수 있다. 이러한 메서드는 모두 동일한 컨트롤러의 @RequestMapping
메서드 전에 호출된다. @ModelAttribute
메서드는 @ControllerAdvice
을 통해 컨트롤러간에 공유 할 수 있다. 자세한 내용은 컨트롤러의 조언 섹션을 참조해라.
@ModelAttribute
메서드는 유연한 메서드 시그너처 있다. 이들은 @RequestMapping
메서드와 같은 인수의 많은 지원한다 ( @ModelAttribute
자체 및 요청 본문에 관련하는 것을 제외한다).
다음 예제에서는 @ModelAttribute
방법을 사용하고 있다.
Java
@ModelAttribute
public void populateModel(@RequestParam String number, Model model) {
model.addAttribute(accountRepository.findAccount(number));
// add more ...
}
Kotlin
@ModelAttribute
fun populateModel(@RequestParam number: String, model: Model) {
model.addAttribute(accountRepository.findAccount(number))
// add more ...
}
다음 예제에서는 하나의 속성만 추가한다.
Java
@ModelAttribute
public Account addAccount(@RequestParam String number) {
return accountRepository.findAccount(number);
}
Kotlin
@ModelAttribute
fun populateModel(@RequestParam number: String, model: Model) {
model.addAttribute(accountRepository.findAccount(number))
// add more ...
}
이름이 명시적으로 지정되지 않은 경우
Conventions(Javadoc)
의 javadoc에 따라 유형에 따라 기본 이름이 선택된다. 오버로드 된addAttribute
메서드를 사용하거나@ModelAttribute
name 속성 (반환 용)를 사용하여 명시적인 이름을 언제든지 지정할 수 있다.
Spring WebFlux은 Spring MVC와 달리 모델의 리액티브 형을 명시 적으로 지원한다 (예를 들어, Mono<Account>
또는 io.reactivex.Single<Account>
). 다음의 예와 같이, @ModelAttribute
인수가 래퍼없이 선언 된 경우, 그 같은 비동기 모델 속성은 @RequestMapping
호출 시점에서 실제 값으로 투명하게 해결 (및 모델 업데이트) 할 수 있다.
Java
@ModelAttribute
public void addAccount(@RequestParam String number) {
Mono<Account> accountMono = accountRepository.findAccount(number);
model.addAttribute("account", accountMono);
}
@PostMapping("/accounts")
public String handle(@ModelAttribute Account account, BindingResult errors) {
// ...
}
Kotlin
import org.springframework.ui.set
@ModelAttribute
fun addAccount(@RequestParam number: String) {
val accountMono: Mono<Account> = accountRepository.findAccount(number)
model["account"] = accountMono
}
@PostMapping("/accounts")
fun handle(@ModelAttribute account: Account, errors: BindingResult): String {
// ...
}
또한 리액티브 형 래퍼를 가진 모델 속성은 뷰의 렌더링 직전에 실제 값으로 해결된다(그리고 모델이 업데이트된다).
@ModelAttribute
이 @RequestMapping
메서드의 메서드 레벨의 어노테이션으로 사용할 수 있다. 이 경우 @RequestMapping
메서드의 반환 값은 모델 속성으로 해석된다. 반환 값이 뷰 이름으로 해석되는 String 경우를 제외하고 이것은 HTML 컨트롤러의 기본 동작이기 때문에 일반적으로 필요하지 않다. @ModelAttribute
다음의 예와 같이 모델 속성 이름의 사용자 정의에도 도움이 된다.
Java
@GetMapping("/accounts/{id}")
@ModelAttribute("myAccount")
public Account handle() {
// ...
return account;
}
Kotlin
@GetMapping("/accounts/{id}")
@ModelAttribute("myAccount")
fun handle(): Account {
// ...
return account
}
1.4.5. DataBinder
웹 MVC
@Controller
또는 @ControllerAdvice
클래스는 WebDataBinder
인스턴스를 초기화하기 위해 @InitBinder
메서드를 가질 수 있다. 이들은 다음과 같은 목적으로 사용된다.
요청 매개 변수 (즉, 양식 데이터 또는 쿼리)를 모델 객체에 바인드한다.
String
기반 요청 값 (요청 매개 변수, 경로 변수 헤더, Cookie 등)를 컨트롤러 메서드 인수 대상 유형으로 변환한다.
HTML 양식을 렌더링 할 때 모델 객체 값을 String 값으로 포맷한다.
@InitBinder
메서드는 컨트롤러 고유의 java.beans.PropertyEditor
또는 Spring Converter
및 Formatter
구성 요소를 등록 할 수 있다. 또한 WebFlux Java 구성 을 사용하여 Converter
및 Formatter
유형을 전 세계적으로 공유되는 FormattingConversionService
에 등록 할 수 있다.
@InitBinder
방법은 @ModelAttribute
(명령 개체) 인수를 제외하고 @RequestMapping
메서드와 같은 인수의 많은 지원한다. 일반적으로 이러한 등록의 WebDataBinder
인수 및 void 반환 값으로 선언된다. 다음 예제에서는 @InitBinder
어노테이션을 사용하고 있다.
Java
@Controller
public class FormController {
@InitBinder // (1)
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}
// ...
}
Kotlin
@Controller
class FormController {
@InitBinder // (1)
fun initBinder(binder: WebDataBinder) {
val dateFormat = SimpleDateFormat("yyyy-MM-dd")
dateFormat.isLenient = false
binder.registerCustomEditor(Date::class.java, CustomDateEditor(dateFormat, false))
}
// ...
}
- (1)
@InitBinder
어노테이션을 사용한다.
또는 공유 FormattingConversionService
를 통해 Formatter 기반 설치를 사용하는 경우 다음의 예와 같이 동일한 접근 방식을 다시 사용하여 컨트롤러 고유의 Formatter
인스턴스를 등록 할 수 있다.
Java
@Controller
public class FormController {
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd")); // (1)
}
// ...
}
Kotlin
@Controller
class FormController {
@InitBinder
fun initBinder(binder: WebDataBinder) {
binder.addCustomFormatter(DateFormatter("yyyy-MM-dd")) // (1)
}
// ...
}
- (1) 사용자 정의 포맷터 (이 경우
DateFormatter
)를 추가한다.
1.4.6. 예외 관리
@Controller
와 @ControllerAdvice
클래스는 컨트롤러 메서드에서 예외를 처리하는 @ExceptionHandler
메서드를 포함 할 수 있다. 다음의 예제는 이러한 핸들러 메서드가 포함되어 있다.
Java
@Controller
public class SimpleController {
// ...
@ExceptionHandler // (1)
public ResponseEntity<String> handle(IOException ex) {
// ...
}
}
Kotlin
@Controller
class SimpleController {
// ...
@ExceptionHandler // (1)
fun handle(ex: IOException): ResponseEntity<String> {
// ...
}
}
- (1)
@ExceptionHandler
선언.
예외는 전파되는 최상위 예외 (즉, 직접 IOException
이 발생되는) 또는 최상위 래퍼 예외의 직접적인 원인(예를 들어, IllegalStateException
내 랩 된 IOException
)과 일치한다.
예외의 유형을 일치 시키려면 이전의 예와 같이 대상의 예외를 메서드 인수로 선언하는 것이 좋습니다. 또는 어노테이션 선언은 일치하는 예외 유형을 제한 할 수 있다. 일반적으로 인수 서명을 최대한 구체적으로 지정하고 해당 순서로 우선 순위 된 @ControllerAdvice
에서 기본 루트 예외 매핑을 선언하는 것이 좋습니다. 자세한 내용은 MVC 섹션 을 참조해라.
WebFlux의 @ExceptionHandler
메서드는 @RequestMapping
메서드와 같은 메서드 인수와 반환 값을 지원하고 있지만, 요청 내용 및 @ModelAttribute
관련 메서드 인수는 예외이다.
Spring의 @ExceptionHandler
방식 지원 WebFlux는 @RequestMapping
메서드의 HandlerAdapter
의해 제공된다. 자세한 내용은 DispatcherHandler
을 참조해라.
REST API에 대한 예외
REST 서비스의 일반적인 요구 사항은 응답 본문에 오류 정보를 포함하는 것이다. Spring Framework는 응답 내용의 오류 세부 표현이 응용 프로그램 고유이기 때문에 자동으로 그렇게하지 않는다. 단, @RestController
이 ResponseEntity
반환 값을 가지는 @ExceptionHandler
메서드를 사용하여 응답의 상태와 본문을 설정할 수 있다. 그런 메서드를 @ControllerAdvice
클래스로 선언하여 전역에 적용 할 수 있다.
Spring WebFlux는 Spring MVC
ResponseEntityExceptionHandler
에 해당하는 것이 없는 점에 유의해라. 이것은 WebFlux이ResponseStatusException
(또는 그 서브 클래스)만을 생성하고 HTTP 상태 코드로 변환 할 필요가 없기 때문이다.
1.4.7. 컨트롤러 조언(Controller Advice)
일반적으로 @ExceptionHandler
, @InitBinder
, @ModelAttribute
방법은 그들이 선언되는 @Controller
클래스 (또는 클래스 계층 구조)에 적용된다. 이 같은 방법을 (컨트롤러 전체)보다 전체적으로 적용하는 경우 @ControllerAdvice
또는 @RestControllerAdvice
어노테이션이 붙은 클래스에서 선언 할 수 있다.
@ControllerAdvice
에는 @Component
어노테이션이 선엉되어 있다. 이것은 이러한 클래스를 구성 요소 검사 에 의해 Spring Bean으로 등록 할 수 있는 것을 의미한다. @RestControllerAdvice
은 @ControllerAdvice
완 @ResponseBody
모두 어노테이션이 붙은 합성 어노테이션이다. 이것은 본질적으로 @ExceptionHandler
메서드가 메시지 변환(뷰 리졸버 조치 또는 템플릿 렌더링에) 의해 응답 본문에 렌더링되는 것을 의미한다.
시작할 때 @RequestMapping
와 @ExceptionHandler
메서드의 인프라 클래스는 @ControllerAdvice
어노테이션이 선언된 Spring Bean을 감지하고 실행시에 그 방법을 적용한다. 글로벌 @ExceptionHandler
메서드 (@ControllerAdvice
에서) 로컬 메서드 (@Controller
에서)의 후 적용 되고 있다. 대조적으로, 글로벌 @ModelAttribute
및 @InitBinder
메서드는 로컬 메서드의 전에 적용된다.
기본적으로 @ControllerAdvice
메서드는 모든 요청(즉, 모든 컨트롤러)에 적용되지만 다음의 예와 같이 어노테이션 속성을 사용하여 컨트롤러의 하위 집합에 범위를 줄일 수 있다.
Java
// Target all Controllers annotated with @RestController
@ControllerAdvice(annotations = RestController.class)
public class ExampleAdvice1 {}
// Target all Controllers within specific packages
@ControllerAdvice("org.example.controllers")
public class ExampleAdvice2 {}
// Target all Controllers assignable to specific classes
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
public class ExampleAdvice3 {}
Kotlin
// Target all Controllers annotated with @RestController
@ControllerAdvice(annotations = [RestController::class])
public class ExampleAdvice1 {}
// Target all Controllers within specific packages
@ControllerAdvice("org.example.controllers")
public class ExampleAdvice2 {}
// Target all Controllers assignable to specific classes
@ControllerAdvice(assignableTypes = [ControllerInterface::class, AbstractController::class])
public class ExampleAdvice3 {}
이전 예제 셀렉터는 런타임시 평가되고 광범위하게 사용하면 성능에 악영향을 미칠 수 있다. 자세한 내용은 @ControllerAdvice
를 참조해라.