使用 webflux 处理异常并返回正确的 HTTP 代码
Handling exceptions and returning proper HTTP code with webflux
我正在使用 WebFlux 的功能端点。我使用 onErrorResume
:
将服务层发送的异常转换为 HTTP 错误代码
public Mono<String> serviceReturningMonoError() {
return Mono.error(new RuntimeException("error"));
}
public Mono<ServerResponse> handler(ServerRequest request) {
return serviceReturningMonoError().flatMap(e -> ok().syncBody(e))
.onErrorResume( e -> badRequest(e.getMessage()));
}
只要服务 returns 是 Mono,它就可以很好地工作。如果服务返回 Flux,我该怎么办?
public Flux<String> serviceReturningFluxError() {
return Flux.error(new RuntimeException("error"));
}
public Mono<ServerResponse> handler(ServerRequest request) {
???
}
编辑
我尝试了下面的方法,但不幸的是它不起作用。 Flux.error 不由 onErrorResume
处理并传播到框架。当异常在 http 响应的序列化过程中被拆箱时,Spring 引导异常管理捕获它并将其转换为 500。
public Mono<ServerResponse> myHandler(ServerRequest request) {
return ok().contentType(APPLICATION_JSON).body( serviceReturningFluxError(), String.class)
.onErrorResume( exception -> badRequest().build());
}
我真的很惊讶这种行为,这是一个错误吗?
您的第一个示例使用 Mono
(即最多一个值),因此它与 Mono<ServerResponse>
配合得很好 - 该值将在内存中异步解析,并且根据结果我们将 return不同的响应或手动处理业务异常。
在 Flux
的情况下(即 0..N 值),任何给定时间都可能发生错误。
在这种情况下,您可以使用 collectList
运算符将 Flux<String>
转换为 Mono<List<String>>
,并带有一个大警告:所有元素都将缓冲在内存中。如果数据流很重要,或者如果您的 controller/client 依赖流数据,那么这不是最佳选择。
恐怕对于这个问题我没有更好的解决方案,原因如下:由于在 Flux
期间随时可能发生错误,因此无法保证我们可以更改 HTTP 状态和回应:事情可能已经在网络上被刷新了。当使用 Spring MVC 和 returning InputStream
或 Resource
.
时已经是这种情况
Spring 引导错误处理功能尝试写入错误页面并更改 HTTP 状态(请参阅 ErrorWebExceptionHandler
和实施 类),但如果响应已提交,它将记录错误信息并让您知道 HTTP 状态可能是错误的。
我找到了解决此问题的另一种方法,即在 body
方法中捕获异常并将其映射到 ResponseStatusException
public Mono<ServerResponse> myHandler(ServerRequest request) {
return ok().contentType(MediaType.APPLICATION_JSON)
.body( serviceReturningFluxError()
.onErrorMap(RuntimeException.class, e -> new ResponseStatusException( BAD_REQUEST, e.getMessage())), String.class);
}
使用这种方法 Spring 可以正确处理响应和 returns 预期的 HTTP 错误代码。
我正在使用 WebFlux 的功能端点。我使用 onErrorResume
:
public Mono<String> serviceReturningMonoError() {
return Mono.error(new RuntimeException("error"));
}
public Mono<ServerResponse> handler(ServerRequest request) {
return serviceReturningMonoError().flatMap(e -> ok().syncBody(e))
.onErrorResume( e -> badRequest(e.getMessage()));
}
只要服务 returns 是 Mono,它就可以很好地工作。如果服务返回 Flux,我该怎么办?
public Flux<String> serviceReturningFluxError() {
return Flux.error(new RuntimeException("error"));
}
public Mono<ServerResponse> handler(ServerRequest request) {
???
}
编辑
我尝试了下面的方法,但不幸的是它不起作用。 Flux.error 不由 onErrorResume
处理并传播到框架。当异常在 http 响应的序列化过程中被拆箱时,Spring 引导异常管理捕获它并将其转换为 500。
public Mono<ServerResponse> myHandler(ServerRequest request) {
return ok().contentType(APPLICATION_JSON).body( serviceReturningFluxError(), String.class)
.onErrorResume( exception -> badRequest().build());
}
我真的很惊讶这种行为,这是一个错误吗?
您的第一个示例使用 Mono
(即最多一个值),因此它与 Mono<ServerResponse>
配合得很好 - 该值将在内存中异步解析,并且根据结果我们将 return不同的响应或手动处理业务异常。
在 Flux
的情况下(即 0..N 值),任何给定时间都可能发生错误。
在这种情况下,您可以使用 collectList
运算符将 Flux<String>
转换为 Mono<List<String>>
,并带有一个大警告:所有元素都将缓冲在内存中。如果数据流很重要,或者如果您的 controller/client 依赖流数据,那么这不是最佳选择。
恐怕对于这个问题我没有更好的解决方案,原因如下:由于在 Flux
期间随时可能发生错误,因此无法保证我们可以更改 HTTP 状态和回应:事情可能已经在网络上被刷新了。当使用 Spring MVC 和 returning InputStream
或 Resource
.
Spring 引导错误处理功能尝试写入错误页面并更改 HTTP 状态(请参阅 ErrorWebExceptionHandler
和实施 类),但如果响应已提交,它将记录错误信息并让您知道 HTTP 状态可能是错误的。
我找到了解决此问题的另一种方法,即在 body
方法中捕获异常并将其映射到 ResponseStatusException
public Mono<ServerResponse> myHandler(ServerRequest request) {
return ok().contentType(MediaType.APPLICATION_JSON)
.body( serviceReturningFluxError()
.onErrorMap(RuntimeException.class, e -> new ResponseStatusException( BAD_REQUEST, e.getMessage())), String.class);
}
使用这种方法 Spring 可以正确处理响应和 returns 预期的 HTTP 错误代码。