春季反应性网络 – 异常始终包装在500中

huangapple go评论70阅读模式
英文:

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&lt;ServerResponse&gt; goodRoute(Handler handler, ConfigProperties configProperties) {
    log.info(configProperties.toString());
    return
            RouterFunctions.route(RequestPredicates.GET(&quot;/api/v1/integration/ok&quot;),
                    handler::goodEndpoint)
    .and(
            RouterFunctions.route(RequestPredicates.GET(&quot;/api/v1/integration/notfound&quot;) {
                    handler::badEndpoint));
}

-

public Mono&lt;ServerResponse&gt; goodEndpoint(ServerRequest r) {
    return ServerResponse.ok().build();
}

public Mono&lt;ServerResponse&gt; 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&lt;String&gt; badEndpoint() {
    return webClient
            .get()
            .uri(configProperties.getNotfound())
            .retrieve()
            .onStatus(HttpStatus::is4xxClientError, clientResponse -&gt; {
                if(clientResponse.statusCode().equals(HttpStatus.NOT_FOUND)){
                    return Mono.error(new HttpClientErrorException(HttpStatus.NOT_FOUND,
                            &quot;Entity not found.&quot;));
                } 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 &quot;/api/v1/integration/notfound&quot;

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 &quot;/api/v1/integration/notfound&quot; [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 -&gt; {
            if(clientResponse.statusCode().equals(HttpStatus.NOT_FOUND)){
                return Mono.error(new HttpClientErrorException(HttpStatus.NOT_FOUND,
                        &quot;Entity not found.&quot;));
            } else {
                return Mono.error(new HttpClientErrorException(HttpStatus.INTERNAL_SERVER_ERROR));
            }
        })
        .bodyToMono(String.class)
        .onErrorResume( e -&gt; Mono.just(e.getMessage()) );

huangapple
  • 本文由 发表于 2020年8月30日 04:56:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/63651723.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定