Hi Rohit
ok thanks for that explanation. I understand that in your API proxy, when the proxy receives a 303 status code, you are executing an AssignMessage policy in the Target Response PostFlow. In that assignMessage policy you set the statuscode and assign a few headers. Because the AssignTo element is blank, this AssignMessage will affect the “message” variable. Because the policy is attached to in the Target Response PostFlow, “message” will be the response message. So with this policy you are overwriting some things in the response message.
After that policy executes, I expect that Apigee will transmit the response message back to the originating client. That will happen, unless you have something in the ProxyEndpoint logic to modify the response message further. I guess this explains what you are observing. According to my understanding, Apigee and your API proxy are working as documented.
I think you are expecting that by executing an AssignMessage policy, the Apigee API Proxy will follow the redirect implicitly. That is not the case. Executing AssignMessage does not cause an outbound request to be sent. There is no configuration of the AssignMessage policy that you can apply that will cause AssignMessage to “follow a redirect.” Also, there is no setting on the Apigee TargetEndpoint that is analogous to the -L option on the curl command. If you would like Apigee to invoke the URL specified in the Location header in the target response, you need to configure specific, explicit logic to do that. That logic might be a ServiceCallout or a JavaScript policy which uses the httpClient. Either of those things will send out an additional HTTP request. If you do this, then you’ll want to wrap the policy in a Condition such that the ServiceCallout or JavaScript gets called only in the case that there is a Location header in the response. Possibly you may wish to check for 302, 303, 307 etc. as well. AssignMessage will not cause Apigee to invoke an external endpoint. all it does is assign to the internal variable that holds the message content.
If you use ServiceCallout then the configuration should be something like this:
<ServiceCallout name='SC-Follow-Redirect'>
<Request variable='myrequestvariable'>
<Set>
<Headers>
...whatever is appropriate here, if anything...
</Headers>
<!-- redirect should always be GET -->
<Verb>GET</Verb>
<Path>{sc_urlPath}</Path>
</Set>
</Request>
<Response>secondResponse</Response>
<HTTPTargetConnection>
<SSLInfo>
<Enabled>true</Enabled>
<IgnoreValidationErrors>true</IgnoreValidationErrors>
</SSLInfo>
<Properties>
<Property name='success.codes'>2xx, 3xx, 4xx, 5xx</Property>
</Properties>
<URL>https://this-value-will-be-replaced.com</URL>
</HTTPTargetConnection>
</ServiceCallout>
Note the URL and Path elements there. You want that to take a value, determined by the value of the Location header in the response from the first call. You cannot use a variable there. You cannot use a message template. There IS a way to set a URL dynamically for the ServiceCallout. It is described in this post.
Basically, use a JavaScript policy configured like this BEFORE the ServiceCallout policy:
<Javascript name='JS-Assign-Dynamic-URL-for-ServiceCallout' timeLimit='200' >
<Properties>
<Property name='variableContainingSourceUrl'>response.header.Location</Property>
<Property name='scPolicyName'>SC-Follow-Redirect</Property>
</Properties>
<ResourceURL>jsc://assign-Dynamic-URL-for-ServiceCallout.js</ResourceURL>
</Javascript>
And the JS code is like this:
var urlvalue = context.getVariable(properties.variableContainingSourceUrl);
var re = new RegExp('^(https?://[^/]+)(/.*)$');
var match = re.exec(urlvalue);
if (match) {
context.setVariable('servicecallout.' + properties.scPolicyName + '.target.url', match[1]);
context.setVariable('sc_urlPath', match[2]);
}
After that ServiceCallout executes you will have the “response” message which contains the original response with the 303 status. And you will have “secondResponse” (configured in that ServiceCallout policy) which contains the response from the 2nd request. If you want the original client to get the response from the second request, you need to copy it over. This can be done with AssignMessage, like so:
<AssignMessage name='AM-Copy-Response'>
<AssignTo createNew="false" transport="http" type="response">response</AssignTo>
<!-- first, remove everything in the destination response message -->
<Remove/>
<!-- then, "copy everything" -->
<Copy source="secondResponse"/>
</AssignMessage>
Wrapping all that up, the flow should look like this in the TargetEndpoint response flow:
<PostFlow name='postflow'>
<Response>
<Step>
<Condition>NOT (response.header.location = null)</Condition>
<Name>JS-Assign-Dynamic-URL-for-ServiceCallout</Name>
</Step>
<Step>
<Condition>NOT (response.header.location = null)</Condition>
<Name>SC-Follow-Redirect</Name>
</Step>
<Step>
<Condition>NOT (response.header.location = null)</Condition>
<Name>AM-Copy-Response</Name>
</Step>
</Response>
</PostFlow>