1. 개요

이 튜토리얼에서는 실제 예제를 살펴보면서 Spring WebFlux 프로젝트에서 오류를 처리하는 데 사용할 수있는 다양한 전략을 살펴볼 것 입니다.

또한 한 전략을 다른 전략보다 사용하는 것이 유리할 수있는 부분을 지적하고 마지막에 전체 소스 코드에 대한 링크를 제공합니다.

2. 예제 설정

Maven 설정은 Spring Webflux에 대한 소개를 제공하는 이전 기사 와 동일 합니다.

이 예 에서는 사용자 이름을 쿼리 매개 변수 로 사용하고 결과로 "Hello username"반환하는 RESTful 끝점을 사용 합니다 .

먼저 전달 된 처리기에서 handleRequest 라는 메서드로 / hello 요청을 라우팅하는 라우터 함수를 만듭니다 .

@Bean
public RouterFunction<ServerResponse> routeRequest(Handler handler) {
    return RouterFunctions.route(RequestPredicates.GET("/hello")
      .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), 
        handler::handleRequest);
    }

다음으로, sayHello () 메서드를 호출하고 그 결과를 ServerResponse 본문 에 포함 / 반환하는 방법을 찾는 handleRequest () 메서드를 정의합니다 .

public Mono<ServerResponse> handleRequest(ServerRequest request) {
    return 
      //...
        sayHello(request)
      //...
}

마지막으로 sayHello () 메서드는 "Hello" 문자열 과 사용자 이름 을 연결하는 간단한 유틸리티 메서드입니다 .

private Mono<String> sayHello(ServerRequest request) {
    //...
    return Mono.just("Hello, " + request.queryParam("name").get());
    //...
}

사용자 이름이 요청의 일부로 존재하는 한 예를 들어 엔드 포인트가 "/ hello? username = Tonni " 로 호출되는 경우이 엔드 포인트는 항상 올바르게 작동합니다.

그러나 사용자 이름을 지정하지 않고 동일한 끝점 (예 : "/ hello") 을 호출 하면 예외가 발생합니다.

아래에서 WebFlux에서이 예외를 처리하기 위해 코드를 재구성 할 수있는 위치와 방법을 살펴 보겠습니다.

3. 기능 수준에서 오류 처리

기능 수준에서 오류를 처리하기 위해 MonoFlux API에 내장 된 두 가지 주요 연산자가 있습니다.

이들과 그 사용법을 간략하게 살펴 보겠습니다.

3.1. onErrorReturn으로 오류 처리

onErrorReturn ()사용 하여 오류가 발생할 때마다 정적 기본값을 반환 할 수 있습니다 .

public Mono<ServerResponse> handleRequest(ServerRequest request) {
    return sayHello(request)
      .onErrorReturn("Hello Stranger")
      .flatMap(s -> ServerResponse.ok()
      .contentType(MediaType.TEXT_PLAIN)
      .bodyValue(s));
}

여기서 버그가있는 연결 함수 sayHello () 가 예외를 던질 때마다 정적 "Hello Stranger"를 반환합니다 .

3.2. onErrorResume으로 오류 처리

onErrorResume사용 하여 오류를 처리 할 수있는 세 가지 방법이 있습니다.

  • 동적 대체 값 계산
  • 대체 방법으로 대체 경로 실행
  • 오류를 포착, 래핑 및 다시 던지기 (예 : 맞춤형 비즈니스 예외)

값을 계산하는 방법을 살펴 보겠습니다.

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

여기서 우리는 sayHello () 가 예외를 던질 때마다“Error”문자열에 추가 된 동적으로 얻은 오류 메시지로 구성된 문자열을 반환합니다 .

다음 으로 오류가 발생하면 fallback 메서드를 호출 해 보겠습니다 .

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)));
}

여기서 우리는 sayHello () 가 예외를 던질 때마다 대체 메서드 sayHelloFallback ()을 호출합니다 .

onErrorResume ()을 사용하는 마지막 옵션 예를 들어 NameRequiredException 과 같은 오류포착, 래핑 및 다시 던지는 것입니다.

public Mono<ServerResponse> handleRequest(ServerRequest request) {
    return ServerResponse.ok()
      .body(sayHello(request)
      .onErrorResume(e -> Mono.error(new NameRequiredException(
        HttpStatus.BAD_REQUEST, 
        "username is required", e))), String.class);
}

여기서 우리는 sayHello () 가 예외를 던질 때마다 "username is required"라는 메시지와 함께 커스텀 예외를 던지고 있습니다.

4. 글로벌 수준에서 오류 처리

지금까지 우리가 제시 한 모든 예제는 기능 수준에서 오류 처리를 다루었습니다.

그러나 우리는 글로벌 수준에서 WebFlux 오류를 처리하도록 선택할 수 있습니다. 이렇게하려면 두 단계 만 수행하면됩니다.

  • 전역 오류 응답 속성 사용자 지정
  • 전역 오류 처리기 구현

핸들러가 던지는 예외는 자동으로 HTTP 상태와 JSON 오류 본문으로 변환됩니다. 이를 사용자 정의하려면 DefaultErrorAttributes 클래스확장 하고 getErrorAttributes () 메서드를 재정의하면됩니다 .

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;
    }

}

여기서는 BAD_REQUEST 상태 예외 발생시 오류 속성의 일부로 반환 되는 메시지 : " username is required "를 원합니다 .

다음으로 Global Error Handler를 구현해 보겠습니다 . 이를 위해 Spring은 전역 오류 처리를 확장하고 구현할 수있는 편리한 AbstractErrorWebExceptionHandler 클래스를 제공 합니다.

@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));
    }
}

이 예에서는 전역 오류 처리기의 순서를 -2로 설정했습니다. @Order (-1)에 등록 된 DefaultErrorWebExceptionHandler 보다 더 높은 우선 순위 부여하기 위함입니다.

errorAttributes의 목적은 우리가 웹 예외 핸들러의 생성자를 전달하는 하나의 정확한 복사본이 될 것입니다. 이상적으로는 사용자 정의 된 오류 속성 클래스 여야합니다.

그런 다음 모든 오류 처리 요청을 renderErrorResponse () 메서드 로 라우팅하려고합니다 .

마지막으로 오류 속성을 가져 와서 서버 Response body에 삽입합니다.

그런 다음 오류 세부 정보, HTTP 상태 및 컴퓨터 클라이언트에 대한 예외 메시지가 포함 된 JSON 응답을 생성합니다. 브라우저 클라이언트의 경우 동일한 데이터를 HTML 형식으로 렌더링하는 'whitelabel'오류 처리기가 있습니다. 물론 이것은 사용자 정의 할 수 있습니다.

5. 결론

이 기사에서 우리는 Spring WebFlux 프로젝트에서 오류를 처리하는 데 사용할 수있는 다양한 전략을 살펴보고 한 전략을 다른 전략보다 사용하는 것이 유리할 수있는 부분을 지적했습니다.

약속 대로이 기사와 함께 제공되는 전체 소스 코드는 GitHub에서 사용할 수 있습니다 .