Spring Framework

Spring Webflux - Exception Handle

흥부가귀막혀 2022. 3. 14. 13:09

Exception Handle

Functional Level

  • Handler 에서 함수 수행시 onErrorReturn, onErrorResume 을 정의하여 에러 핸들링 처리를 할 수 있다.

onErrorReturn

public Mono<ServerResponse> handleRequest(ServerRequest request) {
    return sayHello(request)
      .onErrorReturn("Request Fail! But Return Success Status!")
      .flatMap(s -> ServerResponse.ok()
      .contentType(MediaType.TEXT_PLAIN)
      .bodyValue(s));
}

onErrorResume

public Mono<ServerResponse> handleRequest(ServerRequest request) {
    return sayHello(request)
      .flatMap(s -> ServerResponse.ok()
      .contentType(MediaType.TEXT_PLAIN)
      .bodyValue(s))
      .onErrorResume(e -> sayHelloFallback()
         .flatMap(s -> ServerResponse.ok()
               .contentType(MediaType.TEXT_PLAIN)
               .bodyValue(s)
         )
      );
}

Controller Level

  • MVC 와 동일하게 @ExceptionHandler 어노테이션을 통해 error handling 이 가능하다.
@Controller public class SimpleController { 
    // ... 

    @ExceptionHandler 
    public ResponseEntity<String> handle(IOException ex) { 
         // ... 
    } 
}

⚠️ Spring WebFlux 에서 @ExceptionHandler 를 사용할 경우 request body나 @ModelAttribute에 관련된 매개변수와 반환 값은 사용 불가능하다.

Global Level

  • WebExceptionHandler 를 정의하여 Bean 으로 등록하면 모든 요청에 대한 Exception Handling 이 가능하다.
  • Spring WebFlux 에서는 아래의 WebExceptionHandler 구현체를 제공한다.
    • ResponseStatusExceptionHandler : ResponseStatusException 유형에 대한 예외 처리
    • WebFluxResponseStatusExceptionHandler : @ResponseStatus 가 선언된 경우에 대한 예외처리. ResponseStatusExceptionHandler의 확장
    • DefaultErrorWebExceptionHandler : Exception Handle 에 대한 기본적인 기능 제공
  • 직접 WebExceptionHandler 를 구현하고 싶다면 AbstractErrorWebExceptionHandler  DefaultErrorWebExceptionHandler 를 상속받아 커스터마이징 하여 Bean 으로 등록한다.
@Component
@Order(-2)
public class GlobalErrorWebExceptionHandler extends 
    AbstractErrorWebExceptionHandler {

    // constructors

    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(
      ErrorAttributes errorAttributes) {

        return RouterFunctions.route(
          RequestPredicates.all(), this::renderErrorResponse);
    }

    private Mono<ServerResponse> renderErrorResponse(
       ServerRequest request) {

       Map<String, Object> errorPropertiesMap = getErrorAttributes(request, 
         ErrorAttributeOptions.defaults());

       return ServerResponse.status(HttpStatus.BAD_REQUEST)
         .contentType(MediaType.APPLICATION_JSON)
         .body(BodyInserters.fromValue(errorPropertiesMap));
    }
}
 

⚠️ Spring WebFlux 에서는 DefaultErrorWebExceptionHandler 가 @Order(-1) 로 등록이 되기 때문에 DefaultErrorWebExceptionHandler 보다 먼저 error handling 을 하려면 @Order(-2) 를 정의해야한다.

  • 에러 응답에 대한 정의만 필요하다면, ErrorAttributes 혹은 DefaultErrorAttributes 를 상속받은 구현체를 Bean 으로 정의하는 방법도 있다.
@Component
public class GlobalErrorAttributes extends DefaultErrorAttributes {
    
    @Override
    public Map<String, Object> getErrorAttributes(ServerRequest request, 
      ErrorAttributeOptions options) {
        Map<String, Object> map = super.getErrorAttributes(
          request, options);
        map.put("status", HttpStatus.BAD_REQUEST);
        map.put("message", "username is required");
        return map;
    }

}