跨域资源共享 (CORS) 是一种万维网联合会 (W3C) 规范(通常被认为是 HTML5 的一部分),它可让 JavaScript 克服由浏览器施加的同域策略安全限制。所谓同域策略,就是 JavaScript 只能对包含网页的同 一个域进行 AJAX 回调(其中,“域”就是主机名、协议和端口号的组合)。例如, http://foo.com 中某个网页上的 JavaScript 无法对 http://bar.com(或 http://www.foo.com、 https://foo.com 或 http://foo.com:999 等)进行 AJAX 调用。
CORS 可让服务器指明允许哪些域对它们进行调用,从而放宽这种限制。CORS 是由浏览器强制执行的,并 且必须在服务器上实现,而最新版本的 ASP.NET Web API 2 全面支持 CORS。通过 Web API 2,您可以对策略 进行配置以允许不同域的 JavaScript 客户端访问您的 API。
CORS 基本信息
由于 Web API 完全按照该规范来实现,因此,为了使用 Web API 中的新 CORS 功能,详细了解 CORS 本 身将大有帮助。这些详细内容现在看起来可能都是理论之谈,但对于以后了解 Web API 中的可用设置来说将 十分有用:在您调试 CORS 时,这些内容有助于您更快速地解决问题。
CORS 的一般机制是,当 JavaScript 尝试进行跨域 AJAX 调用时,浏览器会通过在 HTTP 请求中发送标头 (如“Origin”)来“询问”服务器是否允许进行这样的调用。服务器通过在响应中返 回 HTTP 标头(如“Access-Control-Allow-Origin”)指明允许的操作。这种权限检查将针对客 户端调用的每个不同 URL 进行,这就意味着不同的 URL 可以具有不同权限。
除域之外,CORS 还可以让服务器指明允许使用的 HTTP 方法、客户端可以发送的 HTTP 请求标头、客户端 可以读取的 HTTP 响应标头以及是否允许浏览器自动发送或接收凭据(Cookie 或授权标头)。其他请求和响 应标头指明允许使用其中的哪些功能。图 1 总结了这些标头(请注意,一些功能没有在响 应中发送的标头,仅有响应)。
图 1 CORS HTTP 标头
浏览器可通过两种不同的方式向服务器请求这些权限:简单 CORS 请求和预检 CORS 请求。
简单 CORS 请求。下面是简单 CORS 请求的示例:
POST http://localhost/WebApiCorsServer/Resources/ HTTP/1.1
Host: localhost
Accept: */*
Origin: http://localhost:55912
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
value1=foo&value2=5
响应如下:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Access-Control-Allow-Origin: http://localhost:55912
Content-Length: 27
{"Value1":"foo","Value2":5}
该请求是从 http://localhost:55912 到 http://localhost 的跨域请求,而浏览器会在该请求中添加一 个“Origin”HTTP 标头以指示服务器的调用域。 服务器用“Access-Control-Allow- Origin”响应标头进行响应,指明允许使用此域。 浏览器强制执行服务器的策略,而 JavaScript 将接 收其正常成功回调。
服务器或者可以使用来自该请求的确切域值做出响应,或者可以使用指明允许使用任何域的 “*”值做出响应。 如果服务器还未允许该调用域,则“Access-Control-Allow- Origin”标头会缺失,并会引起该调用 JavaScript 的错误回调。
请注意,进行简单 CORS 请求时,仍会对服务器进行调用。 如果您对 CORS 了解不深,可能会觉得奇怪, 但这种行为无异于浏览器已构造 <form> 元素并进行正常 POST 请求的情况。 CORS 不会阻止对服务器 的调用;但它会阻止调用 JavaScript 接收结果。 如果您要阻止调用方调用服务器,则需要在服务器代码中 实现某种授权(可能要使用 [Authorize] 授权筛选器属性)。
前面的示例称为简单 CORS 请求,因为来自客户端的 AJAX 调用的类型或者是 GET,或者是 POST; Content-Type 是 application/x-www-form-urlencoded、multipart/form-data 或 text/plain 中的一 个;未发送任何其他请求标头。 如果 AJAX 调用是另一个 HTTP 方法,Content-Type 是某个其他值,或者客 户端想要发送其他请求标头,则会将该请求视为预检请求。 预检请求的机制略有不同。
预检 CORS 请求。如果 AJAX 调用不是简单请求,则它需要一个预检 CORS 请求,此请求只不过是一个发 送到服务器以获取权限的附加 HTTP 请求。 此预检请求由浏览器自动发出,并使用 OPTIONS HTTP 方法。 如 果服务器成功响应该预检请求并授予权限,则浏览器将执行 JavaScript 正在尝试执行的实际 AJAX 调用。
如果关心的是性能问题(以及何时出现性能问题),则浏览器可通过在预检响应中包含 Access-Control- Max-Age 标头来缓存此预检请求的结果。 该值包含可对权限进行缓存的秒数。