英文:
Spring Reactive Web - Exceptions are always wrapped in 500
问题
使用一个非常简单的项目,错误处理并不清晰。
public class WebfluxApplication {
public static void main(String[] args) {
SpringApplication.run(WebfluxApplication.class, args);
}
}
public class EndpointRouter {
@Bean
public WebClient webClient() {
return WebClient.builder().build();
}
@Bean
public RouterFunction<ServerResponse> goodRoute(Handler handler, ConfigProperties configProperties) {
log.info(configProperties.toString());
return RouterFunctions.route(RequestPredicates.GET("/api/v1/integration/ok"),
handler::goodEndpoint)
.and(
RouterFunctions.route(RequestPredicates.GET("/api/v1/integration/notfound"),
handler::badEndpoint));
}
}
public Mono<ServerResponse> goodEndpoint(ServerRequest r) {
return ServerResponse.ok().build();
}
public Mono<ServerResponse> badEndpoint(ServerRequest r) {
var result = service.badEndpoint();
return ServerResponse
.ok()
.body(result, String.class);
}
public class Service {
private final WebClient webClient;
private final ConfigProperties configProperties;
public Service(WebClient webClient, ConfigProperties configurationProperties) {
this.webClient = webClient;
this.configProperties = configurationProperties;
}
public Mono<String> badEndpoint() {
return webClient
.get()
.uri(configProperties.getNotfound())
.retrieve()
.onStatus(HttpStatus::is4xxClientError, clientResponse -> {
if(clientResponse.statusCode().equals(HttpStatus.NOT_FOUND)){
return Mono.error(new HttpClientErrorException(HttpStatus.NOT_FOUND,
"Entity not found."));
} else {
return Mono.error(new HttpClientErrorException(HttpStatus.INTERNAL_SERVER_ERROR));
}
})
.bodyToMono(String.class);
}
}
从阅读文档来看,我不应该为整个项目设置全局错误处理程序,我应该能够处理 404 错误,并将 404 返回给原始调用方。
这是输出:
2020-08-29 16:52:46.301 ERROR 25020 --- [ctor-http-nio-4] a.w.r.e.AbstractErrorWebExceptionHandler : [b339763e-1] 500 Server Error for HTTP GET "/api/v1/integration/notfound"
org.springframework.web.client.HttpClientErrorException: 404 Entity not found.
at com.stevenpg.restperformance.webflux.Service.lambda$badEndpoint$0(Service.java:30) ~[main/:na]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ 404 from GET https://httpbin.org/status/404 [DefaultWebClient]
|_ checkpoint ⇢ Handler com.stevenpg.restperformance.webflux.EndpointRouter$$Lambda$445/0x00000008003ae040@7799b58 [DispatcherHandler]
|_ checkpoint ⇢ HTTP GET "/api/v1/integration/notfound" [ExceptionHandlingWebHandler]
我还尝试过在服务的 Mono 上使用 onErrorResume,但它从不正确工作,需要返回 Mono<ServerResponse> 而不是 Mono<String>。
文档和 Stack Overflow 上没有太多/任何关于在 RouterFunction 中进行 WebClient 调用并处理不同类型响应的示例。
英文:
With a very simple project, error handling is unclear.
public class WebfluxApplication {
public static void main(String[] args) {
SpringApplication.run(WebfluxApplication.class, args);
}
}
-
public class EndpointRouter
{
@Bean
public WebClient webClient() {
return WebClient.builder().build();
}
@Bean
public RouterFunction<ServerResponse> goodRoute(Handler handler, ConfigProperties configProperties) {
log.info(configProperties.toString());
return
RouterFunctions.route(RequestPredicates.GET("/api/v1/integration/ok"),
handler::goodEndpoint)
.and(
RouterFunctions.route(RequestPredicates.GET("/api/v1/integration/notfound") {
handler::badEndpoint));
}
-
public Mono<ServerResponse> goodEndpoint(ServerRequest r) {
return ServerResponse.ok().build();
}
public Mono<ServerResponse> badEndpoint(ServerRequest r) {
var result = service.badEndpoint();
return ServerResponse
.ok()
.body(result, String.class);
}
-
public class Service
{
private final WebClient webClient;
private final ConfigProperties configProperties;
public Service(WebClient webClient, ConfigProperties configurationProperties) {
this.webClient = webClient;
this.configProperties = configurationProperties;
}
public Mono<String> badEndpoint() {
return webClient
.get()
.uri(configProperties.getNotfound())
.retrieve()
.onStatus(HttpStatus::is4xxClientError, clientResponse -> {
if(clientResponse.statusCode().equals(HttpStatus.NOT_FOUND)){
return Mono.error(new HttpClientErrorException(HttpStatus.NOT_FOUND,
"Entity not found."));
} else {
return Mono.error(new HttpClientErrorException(HttpStatus.INTERNAL_SERVER_ERROR));
}
})
.bodyToMono(String.class);
}
From reading the docs, I shouldn't need to set up a Global error handler for the entire project, I should be able to handle the 404, and return a 404 back to the original caller.
This is the output
2020-08-29 16:52:46.301 ERROR 25020 --- [ctor-http-nio-4] a.w.r.e.AbstractErrorWebExceptionHandler : [b339763e-1] 500 Server Error for HTTP GET "/api/v1/integration/notfound"
org.springframework.web.client.HttpClientErrorException: 404 Entity not found.
at com.stevenpg.restperformance.webflux.Service.lambda$badEndpoint$0(Service.java:30) ~[main/:na]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ 404 from GET https://httpbin.org/status/404 [DefaultWebClient]
|_ checkpoint ⇢ Handler com.stevenpg.restperformance.webflux.EndpointRouter$$Lambda$445/0x00000008003ae040@7799b58 [DispatcherHandler]
|_ checkpoint ⇢ HTTP GET "/api/v1/integration/notfound" [ExceptionHandlingWebHandler]
I've also tried using onErrorResume on my Mono from the service, but it never works correct and requires a return of Mono<ServerResponse> rather than Mono<String>.
The documentation and Stack Overflow don't have many/any examples of making a WebClient call inside a RouterFunction and handling different types of responses.
答案1
得分: 2
只需添加onErrorResume
即可解决您的问题。否则错误将在AbstractErrorWebExceptionHandler
中处理。
return webClient
.get()
.uri(configProperties.getNotfound())
.retrieve()
.onStatus(HttpStatus::is4xxClientError, clientResponse -> {
if (clientResponse.statusCode().equals(HttpStatus.NOT_FOUND)) {
return Mono.error(new HttpClientErrorException(HttpStatus.NOT_FOUND,
"Entity not found."));
} else {
return Mono.error(new HttpClientErrorException(HttpStatus.INTERNAL_SERVER_ERROR));
}
})
.bodyToMono(String.class)
.onErrorResume(e -> Mono.just(e.getMessage()));
英文:
Just adding onErrorResume solves your problem. Otherwise error is handled in AbstractErrorWebExceptionHandler.
return webClient
.get()
.uri(configProperties.getNotfound())
.retrieve()
.onStatus(HttpStatus::is4xxClientError, clientResponse -> {
if(clientResponse.statusCode().equals(HttpStatus.NOT_FOUND)){
return Mono.error(new HttpClientErrorException(HttpStatus.NOT_FOUND,
"Entity not found."));
} else {
return Mono.error(new HttpClientErrorException(HttpStatus.INTERNAL_SERVER_ERROR));
}
})
.bodyToMono(String.class)
.onErrorResume( e -> Mono.just(e.getMessage()) );
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论