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>
Answer by Michael Malloy
· Sep 08, 2014 at 08:57 PM
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
Answer by Andrew Good · Nov 09, 2014 at 11:19 PM
@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,
Answer by Jovani Arzate · Apr 10, 2019 at 06:32 AM
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
Add headers (cors) to 401(etc) invalid token response 5 Answers
API Proxyreurns 200 but Response is empty when we make a call from different Origin 2 Answers
Enable request streaming for a specific route rather than entire proxy 2 Answers
CORs - Securing My Endpoint to only allow Apigee Edge 2 Answers