Get hands-on experience with 20+ free Google Cloud products and $300 in free credit for new customers.

Apigee Pre-Flight Options Requests

Not applicable

I create api proxies and check the box that says "Enable Direct Browser Access for Your API — Allow direct requests from a browser via CORS." but my OPTIONS requests are still failing with :

{
    "fault": {
        "faultstring": "Received 405 Response without Allow Header",
        "detail": {
            "errorcode": "protocol.http.Response405WithoutAllowHeader"
        }
    }
}	

From what I understand about CORS Pre-Flight Options requests, the client first sends the OPTIONS request to the server as a safeguard for "safe" CORS. This request should return a response with the list ofrequest types that are available.

My Question: How do I make it so that Apigee responds correctly to OPTIONS requests and does not pass the OPTIONS request to my api behind the proxy?. If it helps I have AngularJS javascript apps trying to communicate with my Apigee endpoint.

Javascript errors:

OPTIONS http://api.example.com No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://client.example.com' is therefore not allowed access.

XMLHttpRequest cannot load http://api.example.com No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://client.example.com' is therefore not allowed access. 


Default "Add CORS" xml

<AssignMessage async="false" continueOnError="false" enabled="true" name="Add-CORS">
    <DisplayName>Add CORS</DisplayName>
    <FaultRules/>
    <Properties/>
    <Add>
        <Headers>
            <Header name="Access-Control-Allow-Origin">*</Header>
            <Header name="Access-Control-Allow-Headers">origin, x-requested-with, accept</Header>
            <Header name="Access-Control-Max-Age">3628800</Header>
            <Header name="Access-Control-Allow-Methods">GET, PUT, POST, DELETE</Header>
        </Headers>
    </Add>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="response"/>
</AssignMessage>

Default Proxy Endpoints xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ProxyEndpoint name="default">
    <Description/>
    <Flows/>
    <PreFlow name="PreFlow">
        <Request/>
        <Response/>
    </PreFlow>
    <HTTPProxyConnection>
        <BasePath>/v1/cnc</BasePath>
        <VirtualHost>default</VirtualHost>
        <VirtualHost>secure</VirtualHost>
    </HTTPProxyConnection>
    <RouteRule name="default">
        <TargetEndpoint>default</TargetEndpoint>
    </RouteRule>
    <PostFlow name="PostFlow">
        <Request/>
        <Response/>
    </PostFlow>
</ProxyEndpoint>

Default Target Endpoint xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TargetEndpoint name="default">
    <Description/>
    <Flows/>
    <PreFlow name="PreFlow">
        <Request/>
        <Response>
            <Step>
                <Name>Add-CORS</Name>
            </Step>
        </Response>
    </PreFlow>
    <HTTPTargetConnection>
        <URL>http://api.example.com/v1/assets.json</URL>
    </HTTPTargetConnection>
    <PostFlow name="PostFlow">
        <Request/>
        <Response/>
    </PostFlow>
</TargetEndpoint>
Solved Solved
4 6 12.4K
1 ACCEPTED SOLUTION

Not applicable

Since you don't want the OPTIONS request to pass through to the backend API, there are two things needed:

  1. A RouteRule to a null target with condition for the OPTIONS request. Notice there is no TargetEndpoint specified.
  2. A custom flow in the ProxyEndpoint to handle the CORS response. Since the new RouteRule sends the message to a null Target (echoes request back to client), the message will not route to the 'default' TargetEndpoint where the CORS policy currently is defined.

The route rule would look as follows:

<RouteRule name="NoRoute">
    <Condition>request.verb == "OPTIONS"</Condition>
</RouteRule>

An updated version of the ProxyEndpoint would look as follows:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ProxyEndpoint name="default">
    <Description/>
    <Flows>
        <Flow name="OptionsPreFlight">
            <Request/>
            <Response>
                <Step>
                    <Name>Add-CORS</Name>
                </Step>
            </Response>
        <Condition>request.verb == "OPTIONS"</Condition> 
        </Flow>
    </Flows>
    <PreFlow name="PreFlow">
        <Request/>
        <Response/>
    </PreFlow>
    <HTTPProxyConnection>
        <BasePath>/v1/cnc</BasePath>
        <VirtualHost>default</VirtualHost>
        <VirtualHost>secure</VirtualHost>
    </HTTPProxyConnection>
    <RouteRule name="NoRoute">
        <Condition>request.verb == "OPTIONS"</Condition>
    </RouteRule>
    <RouteRule name="default">
        <TargetEndpoint>default</TargetEndpoint>
   </RouteRule>
   <PostFlow name="PostFlow">
        <Request/>
        <Response/>
    </PostFlow>
</ProxyEndpoint>

NOTE: The RouteRules are evaluated in the order specified in the ProxyEnpoint configuration. You should always have the default (no condition) Route at the end. Otherwise, if at the top, it will always match and never evaluate the other Route possibilities.

For now, you'd have to make the change any time you create a new proxy from the UI and need the full CORS support. Apigee needs to enhance the new API proxy builder to address this issue.

After this is resolved, folks may also run into: XMLHttpRequest cannot load <url>. The 'Access-Control-Allow-Origin' header contains the invalid value 'http://localhost:9090, *'. Origin 'http://localhost:9090' is therefore not allowed access. As outlined inaspnetwebstack.codeplex.com/workitem/1539 ... this happens when two "Access-Control-Allow-Origin" headers are getting added to the response. To resolve this, remove Add-CORS step from the TargetEndpoint's PreFlow's Response

View solution in original post

6 REPLIES 6