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. 기능 수준에서 오류 처리
기능 수준에서 오류를 처리하기 위해 Mono 및 Flux 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 프로젝트에서 오류를 처리하는 데 사용할 수있는 다양한 전략을 살펴보고 한 전략을 다른 전략보다 사용하는 것이 유리할 수있는 부분을 지적했습니다.