前面一篇,我们通过在服务器端使用了 header 中 'Access-Control-Allow-Origin' 使得跨域请求可以获取数据。那么是不是使用了这个header 就可以使得所有跨域请求都成功呢?否!
下面我们就来讲一下,浏览器跨域请求的一些限制。
首先,我们把之前的html 页面中ajax 请求用fetch 写一下。如下。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>MyHtml</title>
</head>
<body>
<div>hello world</div>
<div>don't speak</div>
<script>
fetch('http://localhost:8887/', {
method: 'POST',
headers: {
'X-Test-Cors': '123'
}
})
</script>
</body>
</html>
然后,我们启动相同的两个服务器。
在浏览器中输入“http://127.0.0.1:8888/” 会发现如下,报错。错误信息中显示request header 中 x-test-cors 在跨域请求中不被允许。
CORS 预请求
跨域的时候
- 允许的方法:GET, HEAD,POST。其他方法都是默认不允许的,浏览器会有一个预请求的方式去验证。刚刚说的“GET,HEAD,POST” 方法,则是不需要预请求去验证的。
- 允许的 Content-Type: “text/plain”,"multipart/form-data","application/x-www-form-urlencoded"。其他的Content-Type,也是需要预请求去验证之后才能发送的。
- 其他限制:
1. 请求头限制 (https://fetch.spec.whatwg.org/#cors-safelisted-request-header)
2. XMLHttpRequestUpload 对象均没有注册任何事件监听器 (少用)
3. 请求中没有使用 ReadableStream 对象 (少用)
我们可以看到,之前网页请求,虽然报错,但在network 中,我们还是可以看到有一个请求,如下。并且是返回了信息的。只不过,浏览器因为一些安全策略的原因,忽略了这个请求的返回并报错了。
那么,浏览器是根据什么判断这个请求是否是允许的。是通过Response Headers。
如果我们要允许我们自定义的Header 在请求中发送,那么我们需要一个新的(Response)Header 告诉浏览器,这个操作是允许的。这个header 就是“Access-Control-Allow-Headers”. 如下,我们在Headers 中添加X-Test-Cors。现在原来的ajax 请求就可以正常使用了。
const http = require('http')
const fs = require('fs')
http.createServer(function (request, response) {
console.log('request come', request.url)
response.writeHead(200, {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'X-Test-Cors'
// 'Access-Control-Allow-Origin': 'http://127.0.0.1:8888'
})
response.end('123456')
}).listen(8887)
console.log('serve listening on 8887')
这个时候,我们来看我们的请求,是有两个请求的,一个方法是OPTION,另一个是POST。
OPTION 请求 就是预请求。浏览器通过OPTION 请求返回的内容,判断后面的请求是否允许。
同理,我们也可以设置CORS允许的方法,如下:
'Access-Control-Allow-Methods': 'POST, DELETE, PUT'
浏览器跨域的限制,是为了服务器端的安全。它限制了不同的域名与一些HTTP 方法,同时也提供了一些Header 配置项,使得我们能够在特定条件下实现跨域。
最后一个关于跨域的Header: (下面的代码,代表了允许以这种方式跨域的请求的最长时间为1000s。也就是1000s 内不需要再发送预请求OPTION来验证了,直接发正式的请求即可)
'Access-Control-Max-Age': '1000'
Done.