TechTip : When you need API Gateway Service to respond to both CORS and not CORS requests

Document created by Mark.ODonohue Employee on Dec 5, 2018
Version 1Show Document
  • View in full screen mode

1. Introduction 
 

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 : 
      Access-Control-Allow-Origin: * 

 

 

2. Discussion 

 

The setup of CORS requirement in API Gateway service is discussed here : 

 

Process CORS Request Assertion - CA API Gateway - 9.2 - CA Technologies Documentation 

 

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. 

 

https://developer.mozilla.org/en-US/docs/Glossary/Preflight_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 

 

3. Solution 

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

 

 

 

https://docops.ca.com/ca-api-gateway/9-2/en/policy-assertions/assertion-palette/access-control-assertions/process-cors-request-assertion

4. Test Cases - Chrome Browser 

 

Here we have the API Portal 3.5 generated javascript code, which we've put in a simple test.html page, 

 

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: 

Access-Control-Allow-Origin: null

Access-Control-Allow-Origin: *

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.

post - How to skip the OPTIONS preflight request in AngularJS - Stack Overflow 

 

Some more information on CA DevTest Community Edition: 

https://communities.ca.com/community/ca-devtest-community/pages/sv-community-edition 

we did not really use it here, just simulated its behaviour in setting the Access-Control-Allow-Origin header. 

 

 

Cheers - Mark

3 people found this helpful

Attachments

    Outcomes