Как настроить ответ в весенней интеграции с помощью WebFlux при возникновении определенной ошибки?

#spring-integration #spring-webflux #spring-integration-dsl

Вопрос:

Я пытаюсь разработать WebClient основанный подход к обработке и обогащению с использованием подпотока при WebFlux.outboundGateway вызове.

Мое намерение состоит в том, чтобы обработать случай, когда HTTP/404 происходит на удаленном конце. В этом случае я обработаю ошибку с помощью известного документа, который можно обработать по потоку и направить соответствующим образом.

 .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")
)
 

Я получаю ошибку компиляции, аналогичную следующей:

 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>)
 

Я даже не знаю, правильно ли я подхожу к этому. Любой совет был бы очень признателен.

Чтобы помочь в этом, я опубликовал весь код https://github.com/djgraff209/so68637283.

ПРАВКА 8/6:

Спасибо @artem-bilan за отзывы по этому поводу. Первый момент заключался в том, что второй аргумент to onErrorResume не был функцией. Я это исправил.

Далее, совпадение исключений оказалось неправильным. WebClientResponseException Был брошен общий, а не более конкретный WebClientResponseException.NotFound , на что я и надеялся.

Я обновил логику следующим образом. Этот код доступен в указанном проекте GitHub.

     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;
                }
            )
    )
 

Хотя это работает, это не так чисто, как хотелось бы. Я не в восторге от необходимости вводить условие if, чтобы разрешить тип исключения с помощью HttpStatus , а не просто разрешить его для определенного класса.

Это может быть хорошим кандидатом на исправление/улучшение.

Отправлено #3610

Ответ №1:

Я думаю, что это должно быть так:

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

Ожидается , что вторым аргументом onErrorResume() будет a Function , а не константа, как у вас до сих пор.

Комментарии:

1. Я попытался преобразовать это, как вы предложили, но получаю: Type mismatch: cannot convert from Mono<Object> to Mono<? extends capture#3-of ?>

2. Да… Я понимаю вашу точку зрения. Это Mono.just() должно быть приведено к (Mono) . Смотрите правку к моему ответу. Я вижу решение этой customizeMonoReply() проблемы с помощью дженериков. Таким образом, в будущей версии вам не понадобится этот (Mono) актерский состав.

3. Я только что обновил базу кода на упомянутом главном посте github — обновление в ближайшее время. Я хочу, чтобы вы поняли, как это работает.

4. Видеть .DefaultWebClientDefaultResponseSpec.statusHandlers . Вероятно , вместо этого каким-то образом используется какой-то другой обработчик DEFAULT_STATUS_HANDLER , который ClientResponse::createException выполняет обратный вызов для определенного исключения в зависимости от статуса. Или ваш статус возвращается в каком-то другом формате. Это то, что нам нужно отладить. Вероятно, вы можете поделиться каким-нибудь простым проектом, с которым можно поиграть.

5. Смотрите отредактированный фрагмент кода, как избежать этого if..else в вашем коде.