WebFlux Error Handling
- doOnError : 예외 발생시 특정행위를 수행한다.
- onErrorReturn : 예외 발생시 특정 값을 return 한다.
- onErrorResume : 예외 발생시 다른 Flux형태로 return 한다.
- onErrorContinue : 예외 발생시 멈추지 않고 해당 영역만 skip 한다(별도 처리 하지 않는 이상 정상 응답을 return한다).
- onErrorMap : 예외 발생시 다른 Exception으로 변환한다.
doOnError
Source:@RequestMapping("/error/test")
public Flux<Integer> getNum() {
return Flux.range(0, 5)
.map(x -> {
if (x == 3) {
throw new IllegalArgumentException("invalid num");
}
return x;
})
.doOnError(x -> log.error("num generate error"))
.log();
}
Result:
INFO 5276 --- [ctor-http-nio-2] reactor.Flux.PeekFuseable.1 : | onSubscribe([Fuseable] FluxPeekFuseable.PeekFuseableSubscriber)
INFO 5276 --- [ctor-http-nio-2] reactor.Flux.PeekFuseable.1 : | request(unbounded)
INFO 5276 --- [ctor-http-nio-2] reactor.Flux.PeekFuseable.1 : | onNext(0)
INFO 5276 --- [ctor-http-nio-2] reactor.Flux.PeekFuseable.1 : | onNext(1)
INFO 5276 --- [ctor-http-nio-2] reactor.Flux.PeekFuseable.1 : | onNext(2)
ERROR 5276 --- [ctor-http-nio-2] c.e.demo.r4.controller.R4Controller : num generate error
ERROR 5276 --- [ctor-http-nio-2] reactor.Flux.PeekFuseable.1 : | onError(java.lang.IllegalArgumentException: invalid num)
ERROR 5276 --- [ctor-http-nio-2] reactor.Flux.PeekFuseable.1 :
onErrorReturn
Source:
@RequestMapping("/error/test")
public Flux<Integer> getNum() {
return Flux.range(0, 5)
.map(x -> {
if (x == 3) {
throw new IllegalArgumentException("invalid num");
}
return x;
})
.onErrorReturn(99)
.log();
}
Result:
INFO 12068 --- [ctor-http-nio-2] reactor.Flux.OnErrorResume.1 : onSubscribe(FluxOnErrorResume.ResumeSubscriber)
INFO 12068 --- [ctor-http-nio-2] reactor.Flux.OnErrorResume.1 : request(unbounded)
INFO 12068 --- [ctor-http-nio-2] reactor.Flux.OnErrorResume.1 : onNext(0)
INFO 12068 --- [ctor-http-nio-2] reactor.Flux.OnErrorResume.1 : onNext(1)
INFO 12068 --- [ctor-http-nio-2] reactor.Flux.OnErrorResume.1 : onNext(2)
INFO 12068 --- [ctor-http-nio-2] reactor.Flux.OnErrorResume.1 : onNext(99)
INFO 12068 --- [ctor-http-nio-2] reactor.Flux.OnErrorResume.1 : onComplete()
onErrorResume
Source:
@RequestMapping("/error/test")
public Flux<Integer> getNum() {
return Flux.range(0, 5)
.map(x -> {
if (x == 3) {
throw new IllegalArgumentException("invalid num");
}
return x;
})
.onErrorResume(exec -> {
log.error("num generate error");
return Mono.just(99); //어떤 값으로 return을 할지 상황에 맞게 지정한다.
})
.log();
}
Result:
INFO 8444 --- [ctor-http-nio-2] reactor.Flux.OnErrorResume.1 : onSubscribe(FluxOnErrorResume.ResumeSubscriber)
INFO 8444 --- [ctor-http-nio-2] reactor.Flux.OnErrorResume.1 : request(unbounded)
INFO 8444 --- [ctor-http-nio-2] reactor.Flux.OnErrorResume.1 : onNext(0)
INFO 8444 --- [ctor-http-nio-2] reactor.Flux.OnErrorResume.1 : onNext(1)
INFO 8444 --- [ctor-http-nio-2] reactor.Flux.OnErrorResume.1 : onNext(2)
ERROR 8444 --- [ctor-http-nio-2] c.e.demo.r4.controller.R4Controller : num generate error
INFO 8444 --- [ctor-http-nio-2] reactor.Flux.OnErrorResume.1 : onNext(99)
INFO 8444 --- [ctor-http-nio-2] reactor.Flux.OnErrorResume.1 : onComplete()
OnErrorContinue
Source:
@RequestMapping("/error/test")
public Flux<Integer> getNum() {
return Flux.range(0, 5)
.map(x -> {
if (x == 3) {
throw new IllegalArgumentException("invalid num");
}
return x;
})
.onErrorContinue((exec, num) ->
log.error("num generate error, number is : {}, error msg : {}", num, exec.getMessage()))
.log();
}
Result:
INFO 9728 --- [ctor-http-nio-2] reactor.Flux.ContextStart.1 : | onSubscribe([Fuseable] FluxContextStart.ContextStartSubscriber)
INFO 9728 --- [ctor-http-nio-2] reactor.Flux.ContextStart.1 : | request(unbounded)
INFO 9728 --- [ctor-http-nio-2] reactor.Flux.ContextStart.1 : | onNext(0)
INFO 9728 --- [ctor-http-nio-2] reactor.Flux.ContextStart.1 : | onNext(1)
INFO 9728 --- [ctor-http-nio-2] reactor.Flux.ContextStart.1 : | onNext(2)
ERROR 9728 --- [ctor-http-nio-2] c.e.demo.r4.controller.R4Controller : num generate error, number is : 3, error msg : invalid num
INFO 9728 --- [ctor-http-nio-2] reactor.Flux.ContextStart.1 : | onNext(4)
INFO 9728 --- [ctor-http-nio-2] reactor.Flux.ContextStart.1 : | onComplete()
onErrorMap
Source:
@RequestMapping("/error/test")
public Flux<Integer> getNum() {
return Flux.range(0, 5)
.map(x -> {
if (x == 3) {
throw new IllegalArgumentException("invalid num");
}
return x;
})
.onErrorMap(exec -> {
log.error("num generate error, error msg : {}", exec.getMessage());
throw new RuntimeException("change error type");
})
.log();
}
Result:
INFO 14204 --- [ctor-http-nio-2] reactor.Flux.OnErrorResume.1 : onSubscribe(FluxOnErrorResume.ResumeSubscriber)
INFO 14204 --- [ctor-http-nio-2] reactor.Flux.OnErrorResume.1 : request(unbounded)
INFO 14204 --- [ctor-http-nio-2] reactor.Flux.OnErrorResume.1 : onNext(0)
INFO 14204 --- [ctor-http-nio-2] reactor.Flux.OnErrorResume.1 : onNext(1)
INFO 14204 --- [ctor-http-nio-2] reactor.Flux.OnErrorResume.1 : onNext(2)
ERROR 14204 --- [ctor-http-nio-2] c.e.demo.r4.controller.R4Controller : num generate error, error msg : invalid num
ERROR 14204 --- [ctor-http-nio-2] reactor.Flux.OnErrorResume.1 : onError(java.lang.RuntimeException: change error type)
[참고링크]
[Webflux Tip] 3. Webflux Error 처리! . (n.d.). https://akageun.github.io/2019/07/26/spring-webflux-tip-3.html.