<ph type="x-smartling-placeholder"></ph>
您正在查看 Apigee Edge 文档。
转到
Apigee X 文档。 信息
CORS(跨源资源共享)是一种标准机制,允许在网页中执行的 JavaScript XMLHttpRequest (XHR) 调用与来自非源网域的资源进行交互。CORS 是通常针对适用于所有浏览器强制执行的“同域政策”实现的解决方案。例如,如果您从浏览器执行的 JavaScript 代码向 Twitter API 发出 XHR 调用,则该调用将失败。这是因为为浏览器提供网页的网域与为 Twitter API 提供服务的网域不同。CORS 针对这个问题提供了一个解决方案,允许服务器在希望提供跨域资源共享的情况下“选择加入”。
视频:观看一段简短视频,了解如何在 API 代理上启用 CORS。
CORS 的典型用例
以下 JQuery 代码会调用虚构的目标服务。如果在浏览器(网页)的上下文内执行,则调用将因同域政策而失败:
<script> var url = "http://service.example.com"; $(document).ready(function(){ $("button").click(function(){ $.ajax({ type:"GET", url:url, async:true, dataType: "json", success: function(json) { // Parse the response. // Do other things. }, error: function(xhr, status, err) { // This is where we end up! } }); }); }); </script>
此问题的一种解决方案是创建一个 Apigee API 代理,以便在后端调用服务 API。请注意,Edge 位于客户端(在本例中为浏览器)和后端之间 API(服务)。由于 API 代理在服务器上执行,而不是在浏览器中执行,因此等于能够成功调用服务。然后,只需将 CORS 标头附加到 TargetEndpoint 响应即可。只要浏览器支持 CORS,这些标头就会向浏览器表明可以“放宽”其同域政策,从而允许跨域 API 调用取得成功。
创建支持 CORS 的代理后,您可以在客户端代码中调用 API 代理网址,而不是后端服务。例如:
<script> var url = "http://myorg-test.apigee.net/v1/example"; $(document).ready(function(){ $("button").click(function(){ $.ajax({ type:"GET", url:url, async:true, dataType: "json", success: function(json) { // Parse the response. // Do other things. }, error: function(xhr, status, err) { // This time, we do not end up here! } }); }); }); </script>
将 CORS 政策附加到新的 API 代理
您可以向 API 代理添加 CORS 支持,只需在创建 API 代理时附加“添加 CORS”政策即可。如需添加此政策,请在“构建代理”向导的“安全性”页面中选中添加 CORS 标头复选框。
选中此复选框后,会将名为 Add CORS 的政策自动添加到系统中,并附加到 TargetEndpoint 响应 PreFlow 中,如下图所示:
添加 CORS 政策作为 AssignMessage 政策实施, 向响应添加相应的标头。 基本上,标头可以告知浏览器它将与哪些域共享资源、它接受的方法等。如需详细了解这些 CORS 标头,请参阅跨域资源共享 W3C 建议。
您应按如下方式修改政策:
- 将
content-type
和authorization
标头(支持基本身份验证或 OAuth2 时需要)添加到Access-Control-Allow-Headers
标头中,如以下代码段所示。 - 对于 OAuth2 身份验证,您可能需要采取一些措施来纠正不符合 RFC 标准的行为。
- 建议您使用
<Set>
设置 CORS 标头,而不是<Add>
,如下面的代码段所示。使用<Add>
时,如果Access-Control-Allow-Origin
标头已存在,您会收到以下错误:The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed.
有关详情,请参见 CORS 错误:标头包含多个值“*, *”,但只允许有一个值。
<AssignMessage async="false" continueOnError="false" enabled="true" name="add-cors"> <DisplayName>Add CORS</DisplayName> <FaultRules/> <Properties/> <Set> <Headers> <Header name="Access-Control-Allow-Origin">{request.header.origin}</Header> <Header name="Access-Control-Allow-Headers">origin, x-requested-with, accept, content-type, authorization</Header> <Header name="Access-Control-Max-Age">3628800</Header> <Header name="Access-Control-Allow-Methods">GET, PUT, POST, DELETE</Header> </Headers> </Set> <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables> <AssignTo createNew="false" transport="http" type="response"/> </AssignMessage>
将 CORS 标头添加到现有代理
您需要手动创建新的“分配消息”政策,并将上一部分中列出的“添加 CORS”政策复制到其中。然后,将政策附加到 API 代理的 TargetEndpoint 的响应 PreFlow。您可以根据需要修改标头值。如需详细了解如何创建和附加政策,请参阅什么是政策?。
处理 CORS 预检请求
CORS 预检是指向服务器发送请求,以验证其是否支持 CORS。典型的预检响应包括服务器将从中接受 CORS 请求的域、CORS 请求支持的 HTTP 方法的列表、可以用作资源请求一部分的标头、将缓存预检响应的最长时间,等等。如果服务没有指明 CORS 支持,或者不希望接受来自客户端域的跨域请求,系统将强制执行浏览器的跨域政策,并且从客户端发出、与该服务器上托管的资源进行互动的任何跨网域请求都将失败。
CORS 预检请求通常使用 HTTP OPTIONS 方法发出。支持 CORS 的服务器收到 OPTIONS 请求后,它会向客户端返回一组 CORS 标头,以指示其 CORS 支持级别。由于存在此握手,客户端知道允许从非域网域请求的内容。
如需详细了解预检,请参阅跨域资源共享 W3C 建议。此外,您还可以参阅关于 CORS 的众多博客和文章。
Apigee 没有开箱即用的 CORS 预检解决方案,但可以按此部分所述实现。目标是让代理评估条件流中的 OPTIONS 请求。然后,代理可以将相应的响应发送回客户端。
我们来看一个示例流,然后讨论处理预检请求的各个部分:
<?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" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</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" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition> </RouteRule> <RouteRule name="default"> <TargetEndpoint>default</TargetEndpoint> </RouteRule> <PostFlow name="PostFlow"> <Request/> <Response/> </PostFlow> </ProxyEndpoint>
此 ProxyEndpoint 的关键部分如下所示:
- 系统会针对 NULL 目标创建 RouteRule,其中包含 OPTIONS 请求的条件。请注意,没有指定 TargetEndpoint。如果收到 OPTIONS 请求,并且 Origin 和 Access-Control-Request-Method 请求标头不为 null,则代理会立即返回 CORS 标头以响应客户端(绕过实际默认“后端”目标)。如需详细了解流条件和 RouteRule,请参阅包含流变量的条件。
<RouteRule name="NoRoute"> <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition> </RouteRule>
- 创建了一个 OptionsPreFlight 流,以便在收到 OPTIONS 请求且 Origin 和 Access-Control-Control-Request-Method 请求标头不为 null 时,向该流添加 CORS 政策(其中包含 CORS 标头)。
<Flow name="OptionsPreFlight"> <Request/> <Response> <Step> <Name>add-cors</Name> </Step> </Response> <Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition> </Flow>
使用示例 CORS 解决方案
GitHub 上提供一个以共享流的形式实现的 CORS 解决方案。将共享流软件包导入您的环境,并使用流钩子或直接将其附加到 API 代理流。有关详情,请参阅 随示例提供的 CORS-Shared-FLow README 文件。