CORS 的使用
创建两个服务器,进入对应目录,命令行 node server.js,node server2.js,启动服务器。
server.js 会读取 test.html,在8888端口显示,test.html发送跨域请求8887服务器,server2.js通过设置'Access-Control-Allow-Origin': 'http://127.0.0.1:8888'
允许跨域。
浏览器认为localhost和127.0.0.1不同,所以本地用127.0.0.1
// server.js
const http = require('http')
const fs = require('fs')
http.createServer(function (request, response) {
console.log('request come', request.url)
const html = fs.readFileSync('test.html', 'utf8') // 注意设置 utf8,这样读取的是字符串,否则是二进制的数据
response.writeHead(200, {
'Content-Type': 'text/html' // 不设置的话,浏览器会直接显示,不解析;默认浏览器会加上
})
response.end(html)
}).listen(8888)
console.log('server listening on 8888')
复制代码
// server2.js
const http = require('http')
http.createServer(function (request, response) {
console.log('request come', request.url)
response.writeHead(200, { // 不设置这个头,浏览器还是会发送请求,并接收内容,浏览器检查head头,如果没有'Access-Control-Allow-Origin',并且设置为允许,浏览器会把本次请求返回的内容会略掉,并且报错。
'Access-Control-Allow-Origin': 'http://127.0.0.1:8888', // 允许 8888 的域名访问,*也可以(不够安全)
})
response.end('123')
}).listen(8887)
console.log('server listening on 8887')
复制代码
添加多个头
通过判断 request.url 来判断是否添加不同的 head 头。
JSONP
浏览器允许 link img script中的src或href进行跨域请求,JSONP的原理就是在script标签中,加载链接,链接访问了服务器某个请求,并返回了内容,服务器返回的内容是可控的,可以在服务器返回内容中的script标签中写一段可执行的JS代码,然后调用JSONP发起请求的一些内容。
// test.html
<script src="http://127.0.0.1:8887/"></script>
复制代码
这样不设置8887的head头,也可以进行跨域,得到请求内容。
CORS跨域限制
主要包括 methods content-type 和 请求头的限制。
允许方法
默认允许方法 GET HEAD POST,其他的方法 PUT DELETE 默认是不允许的。
允许Content-Type
text/plain
multipart/form-data
application/x-www-form-urlencoded
除了这三种,其他的 Content-Type 也需要使用预请求验证后,才能发送。
其他限制
- 请求头限制,自定义的请求头默认是不允许的,也需要验证;官方文档,关于请求头的详细信息https://fetch.spec.whatwg.org/#cors-safelisted-request-header
- XMLHttpRequestUpload 对象均没有注册任何事件监听器(很少用)
- 请求中没有使用 ReadableStream 对象(很少用)
CORS预请求
demo
8888下的 test.html 发送的请求携带了自定义请求头,从浏览器看请求发送成功,返回状态码200,但是浏览器会报错,不允许跨域,数据虽然返回,但被浏览器忽略了。
// server.js
const http = require('http')
const fs = require('fs')
http.createServer(function (request, response) {
console.log('request come', request.url)
const html = fs.readFileSync('test.html', 'utf8')
response.writeHead(200, {
'Content-Type': 'text/html'
})
response.end(html)
}).listen(8888)
console.log('server listening on 8888')
复制代码
// server2.js
const http = require('http')
http.createServer(function (request, response) {
console.log('request come', request.url)
response.writeHead(200, {
'Access-Control-Allow-Origin': '*'
})
response.end('123')
}).listen(8887)
console.log('server listening on 8887')
复制代码
<script>
fetch('http://localhost:8887', { // 发送请求
method: 'POST',
headers: { // 一个自定义的头
'X-Test-Cors': '123'
}
})
</script>
复制代码
实现预请求
const http = require('http')
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' // 服务端添加允许自定义请求头
})
response.end('123')
}).listen(8887)
console.log('server listening on 8887')
复制代码
查看network,会发现多了一个请求文件localhost,它的 Request Method 请求方法是 OPTIONS,其他的很正常请求一样,通过 OPTIONS 请求获得服务端允许发送请求的认可。之后再实际发送 POST 请求。
允许其他请求方法
const http = require('http')
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-Methods': 'POST, PUT, DELETE' // 允许使用被限制的请求方法
})
response.end('123')
}).listen(8887)
console.log('server listening on 8887')
复制代码
跨域请求时间
比如第一次请求,network观察文件,会有 Method 是 OPTIONS 的预请求文件,再次刷新网页发送请求,这个文件就不会再发送了,不会出现在 network 列表中。
const http = require('http')
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-Methods': 'POST, PUT, DELETE',
'Access-Control-Max-Age': '1000' // 1000s之内,不需要再发送预请求进行验证了,时间内直接发正式请求
})
response.end('123')
}).listen(8887)
console.log('server listening on 8887')
复制代码
浏览器希望通过限制保证服务端的安全