CORS 错误
什么是 CORS?
¥What is CORS?
跨源资源共享 (CORS) 是一种机制,浏览器和 Web 视图(例如为 Capacitor 和 Cordova 提供支持的浏览器和 Web 视图)出于安全原因,用于限制从脚本向不同来源的资源发出的 HTTP 和 HTTPS 请求,主要是为了保护用户的数据 并防止可能危及你的应用的攻击。
¥Cross-Origin Resource Sharing (CORS) is a mechanism that browsers and webviews — like the ones powering Capacitor and Cordova — use to restrict HTTP and HTTPS requests made from scripts to resources in a different origin for security reasons, mainly to protect your user's data and prevent attacks that would compromise your app.
为了知道外部源是否支持 CORS,服务器必须发送一些 特殊标头 供浏览器允许请求。
¥In order to know if an external origin supports CORS, the server has to send some special headers for the browser to allow the requests.
源是为你的 Ionic 应用或外部资源提供服务的协议、域和端口的组合。例如,在 Capacitor 中运行的应用以 capacitor://localhost
(iOS) 或 http://localhost
(Android) 作为其来源。
¥An origin is the combination of the protocol, domain, and port from which your Ionic app or the external resource is served. For example, apps running in Capacitor have capacitor://localhost
(iOS) or http://localhost
(Android) as their origin.
当你的应用的来源(例如 http://localhost:8100
和 ionic serve
)与所请求的资源的来源(例如 https://api.example.com
)不匹配时,浏览器的 同源政策 将生效,并且发出请求需要 CORS。
¥When the origin where your app is served (e.g. http://localhost:8100
with ionic serve
) and the origin of the resource being requested (e.g. https://api.example.com
) don't match, the browser's Same Origin Policy takes effect and CORS is required for the request to be made.
当发出跨域请求但服务器未在响应中返回所需的标头(未启用 CORS)时,CORS 错误在 Web 应用中很常见:
¥CORS errors are common in web apps when a cross-origin request is made but the server doesn't return the required headers in the response (is not CORS-enabled):
XMLHttpRequest 无法加载 https://api.example.com。请求的资源上不存在 '访问控制允许来源' 标头。因此,不允许访问源“http://localhost:8100”。
¥XMLHttpRequest cannot load https://api.example.com. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8100' is therefore not allowed access.
CORS 工作原理
¥How does CORS work
预检请求
¥Request with preflight
默认情况下,当 Web 应用尝试发出跨源请求时,浏览器会在实际请求之前发送预检请求。需要此预检请求,以便了解外部资源是否支持 CORS 以及是否可以安全发送实际请求,因为它可能会影响用户数据。
¥By default, when a web app tries to make a cross-origin request the browser sends a preflight request before the actual request. This preflight request is needed in order to know if the external resource supports CORS and if the actual request can be sent safely, since it may impact user data.
在以下情况下,浏览器会发送预检请求:
¥A preflight request is sent by the browser if:
-
方法是:
¥The method is:
-
PUT
-
DELETE
-
CONNECT
-
OPTIONS
-
TRACE
-
PATCH
-
-
或者如果它的标题不是:
¥Or if it has a header other than:
-
接受
¥Accept
-
接受语言
¥Accept-Language
-
内容语言
¥Content-Language
-
内容类型
¥Content-Type
-
DPR
-
下行
¥Downlink
-
保存数据
¥Save-Data
-
视口宽度
¥Viewport-Width
-
宽度
¥Width
-
-
或者,如果它具有
Content-Type
标头,而不是:¥Or if it has a
Content-Type
header other than:-
应用/x-www-form-urlencoded
¥application/x-www-form-urlencoded
-
多部分/表单数据
¥multipart/form-data
-
文本/纯文本
¥text/plain
-
-
或者如果使用
ReadableStream
或XMLHttpRequestUpload
中的事件监听器。¥Or if a
ReadableStream
or event listeners inXMLHttpRequestUpload
are used.
如果满足上述任何条件,则会向资源 URL 发送带有 OPTIONS
方法的预检请求。
¥If any of the conditions above are met, a preflight request with the OPTIONS
method is sent to the resource URL.
假设我们正在向 https://api.example.com
处的虚构 JSON API 发出 POST
请求,其中 Content-Type
为 application/json
。预检请求将如下所示(为了清楚起见,省略了一些默认标头):
¥Let's suppose we are making a POST
request to a fictional JSON API at https://api.example.com
with a Content-Type
of application/json
. The preflight request would be like this (some default headers omitted for clarity):
OPTIONS / HTTP/1.1
Host: api.example.com
Origin: http://localhost:8100
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type
如果服务器启用了 CORS,它将解析 Access-Control-Request-*
标头并了解 POST
请求正尝试从 http://localhost:8100
使用自定义 Content-Type
发出。
¥If the server is CORS enabled, it will parse the Access-Control-Request-*
headers and understand that a POST
request is trying to be made from http://localhost:8100
with a custom Content-Type
.
然后,服务器将响应此预检,通过使用 Access-Control-Allow-*
标头来允许来源、方法和标头:
¥The server will then respond to this preflight with which origins, methods, and headers are allowed by using the Access-Control-Allow-*
headers:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:8100
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type
如果返回的来源和方法与实际请求中的不匹配,或者使用的任何标头都是不允许的,则该请求将被浏览器阻止,并在控制台中显示错误。否则,请求将在预检后提出。
¥If the returned origin and method don't match the ones from the actual request, or any of the headers used are not allowed, the request will be blocked by the browser and an error will be shown in the console. Otherwise, the request will be made after the preflight.
在我们的示例中,由于 API 需要 JSON,因此所有 POST
请求都将具有 Content-Type: application/json
标头并且始终进行预检。
¥In our example, since the API expects JSON, all POST
requests will have a Content-Type: application/json
header and always be preflighted.
简单的请求
¥Simple requests
如果满足以下所有条件,某些请求始终被认为可以安全发送,并且不需要预检:
¥Some requests are always considered safe to send and don't need a preflight if they meet all of the following conditions:
-
方法是:
¥The method is:
-
GET
-
HEAD
-
POST
-
-
仅包含这些标头:
¥Have only these headers:
-
接受
¥Accept
-
接受语言
¥Accept-Language
-
内容语言
¥Content-Language
-
内容类型
¥Content-Type
-
DPR
-
下行
¥Downlink
-
保存数据
¥Save-Data
-
视口宽度
¥Viewport-Width
-
宽度
¥Width
-
-
Content-Type
标头是:¥The
Content-Type
header is:-
应用/x-www-form-urlencoded
¥application/x-www-form-urlencoded
-
多部分/表单数据
¥multipart/form-data
-
文本/纯文本
¥text/plain
-
-
未使用
ReadableStream
或XMLHttpRequestUpload
中的事件监听器。¥No
ReadableStream
or event listeners inXMLHttpRequestUpload
are used.
在我们的示例 API 中,不需要对 GET
请求进行预检,因为没有发送 JSON 数据,因此应用不需要使用 Content-Type: application/json
标头。它们永远都是简单的请求。
¥In our example API, GET
requests don't need to be preflighted because no JSON data is being sent, and so the app doesn't need to use the Content-Type: application/json
header. They will always be simple requests.
CORS 标头
¥CORS Headers
服务器标头(响应)
¥Server Headers (Response)
标头 | 值 | 描述 |
---|---|---|