In some cases you want a API Service setup to respond to a simple GET or POST request. But the client (Chrome mainly) may decide it wants to use CORS, and send an OPTIONS request to the service.
So to work with any client, we need to be able to respond to both a CORS request and a normal GET request.
In our case, we have a "traceId" header added to the request, and so Chrome decides that is not a "simple" request, and that it needs to send an OPTIONS request to the API Gateway before sending the GET request.
It's a bit of a pain, other clients (case in point here is CURL, and CA API PORTAL when using test api, and many other) won't send an OPTIONS request and just send the GET request.
So we are left with needing a service that will handle both a CORS request and a non CORS request in the same service.
In the case our API Gateway service is and managed by API Portal 3.5, and API Portal explorer gives exmaples of how to call the service in a number of programming languages and also allows the Portal to make a sample call (the sample call from API Portal does not send the OPTIONS request).
And finally to add another small complication our backend server here is CA DevTest Community edition, it does not response to an OPTIONS request, but it adds it's own header to the response of :
The setup of CORS requirement in API Gateway service is discussed here :
Where we have a setup of :
Effectively "Process CORS Request" is run, then we check if it is a preflight (OPTIONS request) and then returns the allowed CORS settings. Otherwise we perform the actual GET or POST request.
But if a raw GET request comes in, without testing via a preceding OPTIONS call, then the "Process CORS Request" can fail, since the GET request does not have the appropriate headers set.
Note: Also remember to allow "OPTIONS" on the service, as it is not enabled by default
In our case we need to handle both CORS and non CORS requests. So we put the "Process CORS Request" in the branch.
It will try the CORS request, if it succeeds, and then if it is a preflight (OPTIONS) request, then we return the template response, and the CORS assertion will have set the correct headers.
But for all other outcomes, ie, if the CORS fails, or CORS passes but is not preflight. Then we fail to the other branch and make the backend call.
In this case our backend call is to localhost:8080/devTest (where we are simulating the response from CA devTest Community edition).
4. Test Cases - Chrome Browser
And are running from Chrome, with developer tools, so we can trace the request. We can see three requests, one for the raw test.html page, and then two requests for the XMLHttpRequest. In this case because of the "traceId" header, Chrome has decided it is not a "simple" request so it sends an OPTIONS request followed by a GET request.
Here is the send and response of the OPTIONS request :
(just a note that Origin: null is what we get for page loaded from local file system, and can be subtly different from Allow-Origin: *)
And the second request is the normal GET request that actually retrieves the data :
And on the API Gateway we have two entries (we added the auditing level) :
5. Test Cases - Curl
Being simpler, CURL just makes one request, here is us running the command:
And the one entry we get in the Gateway Audit logs:
If we had a vanilla "Process CORS Request" the curl use case would have failed.
6. Problem with duplicate Access-Control-Allow-Origin with API Gateway 9.2
As mentioned the backend CA DevTest added a header Access-Control-Allow-Origin: *
And when CORS was not used, we just get the backend response directly as per :
But when we go via CORS ie, using the Chrome browser we got back :
This was with API Gateway 9.3 which has merged, the null value since our original request is from "null" (ie a local page) and the response from the backend server which was "*".
I only mention this as on API Gateway 9.2, the merge did not happen and Chrome recieved back two headers ie:
And Chrome threw an exception.
In our case we modified the "Route Via HTTP" request to not allow the response header from the backend. But similar Manage Transport Properties/Headers could be set to leave only one Access-Control-Allow-Origin: header.
7. Additional Links & Info
We were using Angular, there was some discussion about how to avoid OPTIONS check, but ultimately we needed to handle it since adding the extra "traceId" header pushed Chrome to always make an OPTIONS call.
Some more information on CA DevTest Community Edition:
we did not really use it here, just simulated its behaviour in setting the Access-Control-Allow-Origin header.
Cheers - Mark