Search code examples
spring-integrationspring-webfluxspring-integration-dsl

How to customize response in Spring Integration using WebFlux when a specific error occurs?


I'm trying to work out a WebClient based approach to handling and enrichment using a sub-flow on a WebFlux.outboundGateway call.

My intent is to handle the case where an HTTP/404 occurs on the remote end. In that case, I will handle the error with a known document which can be handled down-stream and routed appropriately.

.enrich( e ->
    e.requestSubFlow(
        sf -> sf.handle(
                WebFlux.outboundGateway("http://example.com/entity/name/{entityName}")
                    .uriVariable("entityName", "payload")
                    .httpMethod(HttpMethod.GET)
                    .expectedResponseType(String.class),
                ec -> ec.customizeMonoReply(
                    (message,mono) ->
                        mono.onErrorResume(
                            WebClientResponseException.NotFound.class,
                            Mono.just("{ \"id\": -1, \"name\": null }")
                                .flatMap(s -> 
                                    ServerResponse.ok()
                                        .contentType(MediaType.APPLICATION_JSON)
                                        .bodyValue(s)
                                )
                        )
                )
            )
    )
    .headerExpression("result", "payload")
)

What I'm getting is a compilation error similar to the following:

The method onErrorResume(Class<E>, Function<? super E,? extends Mono<? extends capture#3-of ?>>) in the type Mono<capture#3-of ?> is not applicable for the arguments (Class<WebClientResponseException.NotFound>, Mono<Object>)

I don't even know if I am approaching this correctly. Any advice would be greatly appreciated.

In order to help with this I've posted the entire code https://github.com/djgraff209/so68637283.

EDIT 8/6:

Thanks to @artem-bilan for feedback on this. First point was that the second argument to onErrorResume was not a function. I have corrected that.

Next up, the exception match turned out to be incorrect. A generic WebClientResponseException was being thrown rather than a more specific WebClientResponseException.NotFound which is what I was hoping for.

I updated the logic as follows. This code is available on the referenced GitHub project.

    ec -> ec.customizeMonoReply(
        (Message<?> message, Mono<?> mono) -> 
            mono.onErrorResume(
                WebClientResponseException.class,
                ex1 -> {
                    Mono<?> exReturn = mono;
                    if( ex1.getStatusCode() == HttpStatus.NOT_FOUND ) {
                        exReturn = Mono.just(defaultPayload);
                    }
                    return (Mono)exReturn;
                }
            )
    )

While this works, it is not as clean as I would like. I am not thrilled with having to put an if condition in there to resolve the exception type by the HttpStatus rather than just resolve it to the specific class.

This might be a good candidate for a bug/enhancement.

Submitted #3610


Solution

  • I think it must be like this:

    mono.onErrorResume(
        WebClientResponseException.class,
        ex1 -> Mono.just(ex1)
                .filter(ex -> ex.getStatusCode() == HttpStatus.NOT_FOUND)
                .map(ex -> "default")
                .switchIfEmpty((Mono) mono))
    

    The second argument of onErrorResume() is expected to be a Function, not a constant as you have so far.