Hello, I have a proxy with more than one ServiceCallouts, and I want to make a generic RaiseFault in case of error in my ServiceCallouts. The problem is that I want to return the status code of my ServiceCallout, and I just can return the content body.
I created this RaiseFault to catch my ServiceCallout errors, as you can see the <serviceCallout>.status.code doesn't work. The only way I could make this working was to put the ServiceCalloutResponse variable on the status code, but by doing this I will have to create a RaiseFault for each ServiceCallout policy.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault async="false" continueOnError="false" enabled="true" name="RF-ServiceCalloutHandler">
<DisplayName>RF-ServiceCalloutErrorHandler</DisplayName>
<FaultRules/>
<Properties/>
<FaultResponse>
<Set>
<Payload contentType="application/json">
{
"type": "SERVICE CALLOUT ERROR",
"message": {ServiceCallout.response}// works
}
</Payload>
<StatusCode>{Dynamic.status.code}</StatusCode> // not working
<ReasonPhrase>Gateway Exception</ReasonPhrase>
</Set>
</FaultResponse>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
<ShortFaultReason>false</ShortFaultReason>
</RaiseFault>
<Step>
<FaultRules/>
<Name>SC-MyServiceCallout</Name>
</Step>
<Step>
<Condition>(calloutResponse.status.code != 200)</Condition>
<Name>RF-ServiceCalloutHandler</Name>
</Step>
Solved! Go to Solution.
(I don't like this solution because it uses regex to get the error code)
I made a JS to get the ServiceCallout error code dynamic.
So, if my callout error code, is not a success.code it throw a exception, then I check on DefaultFaultRule if its a ServiceCallout error.
I used this repo as example:
Github - ServiceCalloutHandler
JS:
const SERVICE_CALLOUT_ERROR = "steps.servicecallout.ExecutionFailed"
const errorBody = JSON.parse(error.content);
const regex = /ResponseCode (\d+)/;
function handleCalloutError() {
const fault = errorBody.fault.faultstring;
const detail = errorBody.fault.detail.errorcode;
var match = fault.match(regex);
var statusCode = match ? match[1] : 500;
return {
isServiceCalloutError: detail == SERVICE_CALLOUT_ERROR,
statusCode: statusCode
}
}
const serviceCalloutError = handleCalloutError()
context.setVariable("is_service_callout_error",serviceCalloutError.isServiceCalloutError);
context.setVariable("service_callout_error_code", serviceCalloutError.statusCode);
RaiseFault:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault async="false" continueOnError="false" enabled="true" name="RF-ServiceCalloutHandler">
<DisplayName>RF-ServiceCalloutErrorHandler</DisplayName>
<FaultRules/>
<Properties/>
<FaultResponse>
<Set>
<Payload contentType="application/json">
{ServiceCallout.response}
</Payload>
<StatusCode>{service_callout_error_code}</StatusCode>
<ReasonPhrase>Gateway Exception</ReasonPhrase>
</Set>
</FaultResponse>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
<ShortFaultReason>false</ShortFaultReason>
</RaiseFault>
Proxy:
<Step>
<FaultRules/>
<Name>JS-ExtractServiceCalloutError</Name>
<Condition>(fault.name Matches "ExecutionFailed")</Condition>
</Step>
<Step>
<FaultRules/>
<Name>RF-ServiceCalloutHandler</Name>
<Condition>(fault.name Matches "ExecutionFailed") and (is_service_callout_error == true)</Condition>
</Step>
Hey, it would help if you can share a Trace with the issue you observe. Is the calloutResponse.status.code not resolving in the RaiseFault policy?
Hello, I tried `calloutResponse.status.code` before but didn't work. The result of `calloutResponse.status.code` is empty as you can see on the image below.
I tried to reproduce the issue you observed and can confirm that calloutResponse.status.code flow variable is available in the RaiseFault policy with the following configuration:
Proxy endpoint:
<ProxyEndpoint name="default">
<PreFlow name="PreFlow">
<Request>
<Step>
<Name>Service-Callout-1</Name>
</Step>
<Step>
<Condition>calloutResponse.status.code != 200</Condition>
<Name>Raise-Fault-1</Name>
</Step>
</Request>
<Response/>
</PreFlow>
...
</ProxyEndpoint>
Service Callout-1:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ServiceCallout async="false" continueOnError="true" enabled="true" name="Service-Callout-1">
<DisplayName>Service Callout-1</DisplayName>
<Properties/>
<Request clearPayload="true" variable="myRequest">
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
</Request>
<Response>calloutResponse</Response>
<HTTPTargetConnection>
<Properties/>
<URL>https://httpbin.org/status/403</URL>
</HTTPTargetConnection>
</ServiceCallout>
RaiseFault:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault async="false" continueOnError="false" enabled="true" name="Raise-Fault-1">
<DisplayName>Raise Fault-1</DisplayName>
<Properties/>
<FaultResponse>
<Set>
<Headers/>
<Payload contentType="text/plain"/>
<StatusCode>{calloutResponse.status.code}</StatusCode>
<ReasonPhrase>Server Error</ReasonPhrase>
</Set>
</FaultResponse>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</RaiseFault>
Hello tks, but the problem is that you are passing the service callout response on the RaiseFault,
<StatusCode>{calloutResponse.status.code}</StatusCode>
<Response>calloutResponse</Response>
Let's say I have 2 servicesCallouts with different <Response>
<!-- Fist -->
<Response>calloutResponse1</Response>
<!-- Second -->
<Response>calloutResponse2</Response>
How my raise fault can get the calloutResponse with error and return the result to the client? with the error body and http status?
because, if I do this It will get only a specific serviceCallout response.
<StatusCode>{calloutResponse.status.code}</StatusCode>
What I'm trying to make is like a ServiceCalloutHandler, that I can put on a FaultRule to get executed on every ServiceCalloutException dynamic
(I don't like this solution because it uses regex to get the error code)
I made a JS to get the ServiceCallout error code dynamic.
So, if my callout error code, is not a success.code it throw a exception, then I check on DefaultFaultRule if its a ServiceCallout error.
I used this repo as example:
Github - ServiceCalloutHandler
JS:
const SERVICE_CALLOUT_ERROR = "steps.servicecallout.ExecutionFailed"
const errorBody = JSON.parse(error.content);
const regex = /ResponseCode (\d+)/;
function handleCalloutError() {
const fault = errorBody.fault.faultstring;
const detail = errorBody.fault.detail.errorcode;
var match = fault.match(regex);
var statusCode = match ? match[1] : 500;
return {
isServiceCalloutError: detail == SERVICE_CALLOUT_ERROR,
statusCode: statusCode
}
}
const serviceCalloutError = handleCalloutError()
context.setVariable("is_service_callout_error",serviceCalloutError.isServiceCalloutError);
context.setVariable("service_callout_error_code", serviceCalloutError.statusCode);
RaiseFault:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault async="false" continueOnError="false" enabled="true" name="RF-ServiceCalloutHandler">
<DisplayName>RF-ServiceCalloutErrorHandler</DisplayName>
<FaultRules/>
<Properties/>
<FaultResponse>
<Set>
<Payload contentType="application/json">
{ServiceCallout.response}
</Payload>
<StatusCode>{service_callout_error_code}</StatusCode>
<ReasonPhrase>Gateway Exception</ReasonPhrase>
</Set>
</FaultResponse>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
<ShortFaultReason>false</ShortFaultReason>
</RaiseFault>
Proxy:
<Step>
<FaultRules/>
<Name>JS-ExtractServiceCalloutError</Name>
<Condition>(fault.name Matches "ExecutionFailed")</Condition>
</Step>
<Step>
<FaultRules/>
<Name>RF-ServiceCalloutHandler</Name>
<Condition>(fault.name Matches "ExecutionFailed") and (is_service_callout_error == true)</Condition>
</Step>
Hi @IvanFreire, thank you so much for engaging in the Apigee forum and sharing the solution that worked for you! We also appreciate the valuable exchange of ideas from other community members.
We hope to see you continue sharing your expertise and supporting others in the forum. Don't forget to check out the product article section for more insights. Looking forward to seeing you around! 👋🏽
Tks, @AlexET