Search code examples
spring-integrationspring-integration-http

Spring integration http rest api call flow understanding


<int:publish-subscribe-channel id="validateChannel" apply-sequence="true">
        <int:interceptors>
            <int:wire-tap channel="validateLogger" />
        </int:interceptors>
    </int:publish-subscribe-channel>
    <int:logging-channel-adapter id="validateLogger" level="INFO" />

    <int:bridge input-channel="validateChannel" output-channel="validateRequestOutputChannel" />

    <int:bridge input-channel="validateChannel" output-channel="externalServiceChannel" />
    <int:channel id="externalServiceChannel" />

I have PublishSubscribeChannel and two sequential subscribers to it. there are calls to 2 external apis.

Step 1 (call to first external api), this api will throw exception or send 200 OK, I want to call second external api(step 2) if this api throws 200 Ok or if throws exception, I want to catch it and throw custom exception to the end user.

<int:service-activator input-channel="validateRequestOutputChannel" ref="sampleTransformer" method="preprocessRequest" output-channel="testServiceRequestChannel"/>
        <int-http:outbound-gateway id="testService"
            url-expression="headers.testServiceURL"
            http-method="POST" request-channel="testServiceRequestChannel" reply-channel="testResponseChannel"
            charset="UTF-8"
            extract-request-payload="true" expected-response-type="java.lang.String"
            request-factory="customHttpRequestFactory"
                                   mapped-request-headers="Content-Type:application/json"
            reply-timeout="5000">
            <int-http:request-handler-advice-chain>
                <bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
                    <property name="onSuccessExpressionString" value="payload.delete()" />
                    <property name="successChannel" ref="afterSuccessFetchChannel" />
                    <property name="failureChannel" ref="afterFailFetchChannel" />
                    <property name="onFailureExpressionString" value="payload + ' was bad, with reason: ' + #exception.cause.message" />
                </bean>
            </int-http:request-handler-advice-chain>
        </int-http:outbound-gateway>

<int:transformer input-channel="afterSuccessFetchChannel" output-channel="goodResultChannel1"
                     expression="'Fetching service : ' + payload + ' details was successful'" />

    <int:transformer input-channel="afterFailFetchChannel" output-channel="badResultChannel1" ref="exceptionTransformer" method="handleErrorResponse"/>
    <int:logging-channel-adapter id="badResultChannel1" level="ERROR"/>

    <int:logging-channel-adapter id="goodResultChannel1" level="INFO" />

Step in between, for the step 2, I am using input channel as externalServiceChannel which is subscribe channel to pub sub channel but I am not able to figure out how to connect output channel of step 1 to input channel of step 2,

I was trying to use,

<int:exception-type-router input-channel="testResponseChannel" default-output-channel="errorRecoveryChannel">
        <int:mapping exception-type="org.springframework.web.client.HttpClientErrorException.Conflict"
                     channel="lockServiceErrors"/>
    </int:exception-type-router>

    <int:chain input-channel="lockServiceErrors" output-channel="validateOutputChannel">
        <int:header-enricher>
            <int:header name="http_statusCode" value="409" />
        </int:header-enricher>
        <int:transformer expression="payload.failedMessage" />
    </int:chain>

but the issue above is

  1. first api sends 200 Ok, its sending its response payload in the message(which I don't want, I want to re use the one from pub sub channel)
  2. I tried using ignoreFailures = true, then in case of exceptions, it suppresses exceptions from the first api and proceed to the second one, but I want to handle exceptions(it does not even call the method from exception transformer).
  3. I tried <property name="onSuccessExpressionString" value="payload.delete()" /> but looks like it does not actually delete the payload.

Could you please help?

Step 2(call to second external api):

<int:chain id="test-chain" input-channel="externalServiceChannel" output-channel="validateOutputChannel">
        <int:transformer ref="sampleTransformer" method="preprocessAPIInfo" />
        <int-http:outbound-gateway id="testService2"
            url-expression="headers.testService2URL"
            http-method="GET"
            extract-request-payload="false"
            expected-response-type="com.bibo.test.UserInfo"
            charset="UTF-8"
            request-factory="customHttpRequestFactory"
            mapped-request-headers="Content-Type:application/json,Accept:application/json"
            reply-timeout="5000">
            <int-http:uri-variable name="userId" expression="headers.userId" />
        </int-http:outbound-gateway>
        <int:transformer ref="sampleTransformer" method="processUserInfo" />
        <int:object-to-json-transformer/>
    </int:chain>

Solution

  • I suggest you to revise you flow without a publish-subscribe channel, but really make it linear: call second service after the first. And if first step fails, it is not going to the second which is really an output of the first.

    To keep an original request access in the second step there is a trick: place this request into headers:

    <int:header-enricher>
        <int:header name="originalRequest" expression="payload"/>
    </int:header-enricher>
    

    Then when you need it in the second step you just do like this:

    <int:transformer expression="headers.originalRequest"/>
    

    The rest of error handling logic is probably OK.