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! Go to Solution.
Since you don't want the OPTIONS request to pass through to the backend API, there are two things needed:
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
Since you don't want the OPTIONS request to pass through to the backend API, there are two things needed:
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
It worked for me also. Thanks a lot 🙂
@mmalloy @mmalloyapigeecom Thanks for this, it is really useful information. I am attempting to do a very similar request but am getting stuck on The 'Access-Control-Allow-Origin' header contains multiple values. I have removed the add-cors from the TargetEndpoints Preflow Response but not having much luck.
I am new to using Apigee so hopefully it is just something really simple I am missing!
Cheers,
hey guys, I implemented something like that and it served me correctly.
In the proxy enpoint we must place in the preflow the next call of a Flowcallout to invoke a sharedflow which will have the policy of CORS
<PreFlow name="PreFlow">
<Request>
<Step>
<Name>FC-CORS</Name>
</Step>
<Step>
<Name>FC-OAuth2</Name>
</Step>
</Request>
<Response/>
</PreFlow>
Definition of flowcallout, where we invoke the sharedflow
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<FlowCallout async="false" continueOnError="false" enabled="true" name="FC-CORS">
<DisplayName>FC-CORS</DisplayName>
<FaultRules/>
<Properties/>
<SharedFlowBundle>OPTIONS-CORS-Headers-Response</SharedFlowBundle>
</FlowCallout>
definition of sharedflow
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SharedFlow name="default">
<Step>
<Name>OPTIONS-CORS-Headers-Response</Name>
<Condition>request.verb == "OPTIONS"</Condition>
</Step>
</SharedFlow>
definition of the policy of raisefull, where we will indicate the headers of Access-Control-Allow-Origin with * that will allow the invocation from our browser
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault async="false" continueOnError="false" enabled="true" name="OPTIONS-CORS-Headers-Response">
<DisplayName>OPTIONS CORS Headers Response</DisplayName>
<Properties/>
<FaultResponse>
<Set>
<Headers>
<Header name="Access-Control-Allow-Origin">*</Header>
<Header name="Access-Control-Allow-Headers">origin,
x-requested-with, accept, ucsb-api-key, ucsb-api-version,
authorization</Header>
<Header name="Access-Control-Max-Age">3628800</Header>
<Header name="Access-Control-Allow-Methods">GET, PUT, POST, DELETE</Header>
</Headers>
<Payload contentType="text/plain"/>
<StatusCode>200</StatusCode>
<ReasonPhrase>OK</ReasonPhrase>
</Set>
</FaultResponse>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</RaiseFault>
Regars
Hey I am still not getting access-control-allow-origin in my response header for the preflight request after following the tutorial here, can someone help me understand what mistake I made with my setup?
Here is my add-cors
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<CORS continueOnError="false" enabled="true" name="add-cors">
<DisplayName>Add CORS</DisplayName>
<AllowOrigins>*</AllowOrigins>
<AllowMethods>GET, PUT, POST, DELETE, OPTIONS</AllowMethods>
<AllowHeaders>Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, Authorization</AllowHeaders>
<ExposeHeaders>*</ExposeHeaders>
<MaxAge>3628800</MaxAge>
<AllowCredentials>false</AllowCredentials>
<GeneratePreflightResponse>true</GeneratePreflightResponse>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
<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>
</CORS>
Here is my proxy endpoint with OptionsPreflight at the front
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ProxyEndpoint name="default">
<Flows>
<Flow name="OptionsPreFlight">
<Request>
<Step>
<Name>add-cors</Name>
</Step>
</Request>
<Response/>
<Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
</Flow>
</Flows>
Here is my no-route for options
<RouteRule name="NoRoute">
<Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>
</RouteRule>
<RouteRule name="default">
<TargetEndpoint>default</TargetEndpoint>
</RouteRule>
I also see
The prior discussion that you are responding to here, is from 2014, and is well out of date. The CORS policy that you showed, which works in Apigee X and hybrid, should not be attached to a conditional flow. It should be attached to the proxy preflow. Also it has no Add element - not sure where you got that.
I published a screencast on this previously.
In short, both the configuration of the CoRS policy, and the way you attach it into the API proxy endpoint, look wrong.
Check the documentation, or the screencast. or both!