简述 HTTP 请求与跨域资源共享 (CORS)
原文标题:An Overview of HTTP Requests & Cross-Origin Resource Sharing (CORS)
原文链接:https://javascript.plainenglish.io/quick-overview-of-http-requests-cross-origin-resource-sharing-cors-db139b41d71
作者:Bilge Demirkaya
URL 简介
以下示例 URL 由 4 个部分组成:
- 服务类型(Scheme):指明将被使用的协议(Protocol)。协议指定数据如何传输以及如何处理请求。当你查看协议时,你就能很好地理解这个 URL 的用途。(例如是带有 SMTP、POP3、IMAP 的电子邮件协议,还是获取和管理 git 仓库的 SSH 请求,或者是针对 Web 的 HTTP 请求)。
- HTTP :默认是在 80 端口运行,它指定请求中的 headers。
- HTTPS :与 HTTP 协议类似,但 HTTPS 被认为是浏览器与服务器之间的安全通信。它与 HTTP 不同之处:
- 默认是在 443 端口运行
- 加密除 IP 请求之外的所有请求或响应 headers
- 主机名(Host name):拥有一个更合适命名的 IP 地址。
- 路径(Path):URL 路径就像你的目录路径。它为用户和搜索引擎提供了解当前所在的部分,例如
/about
部分。这有助于实现更好的搜索引擎优化(SEO)。 - 查询参数(Query parameters):它用于将数据发送到服务器。通常出于营销原因使用它来查看广告的效果。以
?
开始,用&
分隔数据。
注意:由于安全原因,不建议发送带有查询参数的数据(这样每个人都可以看到),并且它有一个字符限制(限制在 2048 个字符以内)。
使用 HTTP 和 HTTPS 协议,我们可以通过其他方法将数据发送到服务器。
请求与响应
当用户在浏览器中输入域名时,浏览器会找到该服务器,并向该服务器发送请求。如果它从服务器成功获取响应,就会在浏览器上呈现相应的页面。
注意:当你使用终端发送请求(例如运行 node index.js
)时,进程是相同的。向服务器发送请求不一定需要浏览器,也可以使用终端。然而,如果响应是 HTML,那么终端不会做任何事情,因为 HTML 只是浏览器的指令。
Headers
浏览器和服务器都需要获取对方的大量信息才能识别对方,并最终发送请求或响应。比如 IP 地址、内容类型(Content-Type)、Cookie、缓存控制(Cache-Control)等。你可以看看完整列表,它们带着 Headers 数据也就是键值对。
在发送请求时,只需要手动设置两个 Headers:内容类型(Content-Type)和授权(Authorization)。虽然你可以设置其它 Headers,但它们一般由浏览器自动处理。
- 内容类型(Content-Type) :当你通过正文向服务器发送(POST、PATCH、PUT 请求)数据时,你需要指定其内容类型,可以是
application/json
、text/html
、image/gif
或video/mpeg.
。 - 授权(Authorization) :这是服务器用来识别用户的。与 cookie headers不同,该 Headers 必须由开发人员在发送请求时手动设置。通常用于 API 请求和 JWT 身份验证。
请求处理
通过互联网发送的每个请求包括 2 个必填部分和 1 个可选部分。
- Request Line(必填):由请求方法(GET、POST、DELETE 等)和路径(从 URL 中提取)组成。
- Headers(必填):上面已经简要说明过。
- Body (可选): 向服务器发出 POST、PUT、PATCH 请求时,需要发送一个 Body ,该 Body 告诉服务器你想要发送什么数据。示例:
axios.post(‘/users’,
{id: “5fddfefc4fbd19494493cd71”, name: "username"} // 这部分是 Body
).then(console.log)
- axios 是一个发送请求的库。浏览器还提供了一个叫做 fetch 的函数,可以用来发送请求。另外还有一个用于发送请求的过时请求库。
- post 是请求方法,表明我们正在向服务器发送信息。可以在这里详细查看 HTTP 请求方法。
/users
是指定你在服务器中发送该请求的确切位置的路径。这个 URL 部分其实叫 API。当一个 API 遵循 REST 模式时,它就变成了 REST API,让开发人员可以快速理解和使用 API。例如像 REST 模式所说的,路径应始终采取复数形式。
REST(Representational State Transfer) 是指表述性状态传递,它是一组设计原则,允许使用 API 和修改服务器上的资源。
- Body 是数据对象本身,因此服务器可以获取该数据。如上所述,除了在浏览器中输入域名外,还有多种方法可以将请求发送到服务器。
AJAX:从浏览器发送请求。如果有人说了解 ajax,这意味着他知道如何从浏览器发送请求。
跨域资源共享(CORS)
OPTIONS 请求也叫做预处理请求(pre-flight requests)目前你看到的响应来自 medium.com 服务器。假设我写了一个 JS 代码,当你在网页浏览这个的时候,它正在向我自己的网站服务器发送一个 POST 请求。这称为跨域请求(Cross-Domain request)。
跨域请求(Cross-Domain request):发送到与你当前所在 url 主机名不同的 url 请求。
例如我想使用 JS 代码从浏览器发送另一个请求到另一个域(另一个服务器),但你会发现这有点难度。出于安全原因,浏览器限制从脚本发起的跨源 HTTP 请求。
同源安全策略默认禁止某些跨域(Cross-Domain)请求,尤其是 Ajax 请求,而始终允许相同来源(Same-Origin)请求。
CORS 定义了浏览器和服务器可以交互的方式,并确定允许跨域请求是否安全。
跨域资源共享(CORS)是基于 HTTP headers的机制,它允许服务器指出浏览器应该允许加载资源的任何其他来源(域、协议或端口)。
跨域请求分析
当浏览器发现域是不同的,它会向该服务器发送一个 OPTIONS 请求,检查请求是否被允许。这个行为与我们开发人员其实并没有什么关系,因为这是浏览器自动进行的行为。然而开发人员可以在发送跨域请求之前,向请求添加一些Headers ,这可能有助于获得服务器的允许。
就像其它浏览器请求一样,Headers 中的一些数据会提供一些信息。例如,通过 OPTIONS 方法发送的 Access-Control-Request-Method
,Access-Control-Request-Headers
headers 会提供一些信息:真实请求何时到来,数据类型是什么,请求方法是什么等。
在这种情况下,服务器可以响应是否接受请求。作为响应,服务器可以发回 Access-Control-Allow-Origin
Headers Access-Control-Allow-Origin: *
,表明资源可以被任何域访问。虽然它允许来自其他域的 GET 请求,但它可能限制 POST 请求。
跨域请求 Response Headers
- Access-Control-Allow-Origin:包含允许发送跨域请求的主机名。如果这与用户所在站点的主机名不匹配,则将拒绝跨域请求。
- Access-Control-Allow-Credentials:如果在响应头中为
true
,则跨域请求将包含 Cookie header。 - Access-Control-Allow-Methods:这是一个逗号分隔的字符串,它告诉浏览器跨域请求中允许使用哪种请求方法。如果请求方法未包含在此 Response Headers 中,则不会发送请求。
使用一段 Node.js 代码设置 headers:
router.options('/api/*', (req, res) => {
res.header('Access-Control-Allow-Credentials', true)
res.header('Access-Control-Allow-Origin', req.headers.origin)
res.header('Access-Control-Allow-Methods', 'GET, PUT, POST, PATCH, DELETE')
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept, Credentials'
)
res.send('ok')
})
总结
CORS 标准意味着,服务器开发人员必须处理新的请求和 Response Headers。他们需要用 Headers 来区分界限,这样才能防止安全漏洞。
在这篇文章中我尝试着以最简明的方式来介绍这些重要的概念,希望对你有所帮助。