首先准备三个文件
server1.js
const http = require('http')
const fs = require('fs')
const { resolve } = require('path')
const app = http.createServer((req,res) => {
const html = fs.readFileSync(resolve(__dirname,'./test.html'),'utf8')
res.writeHead(200,{
'Content-type': 'text/html'
})
res.end(html)
})
app.listen(8888)
test.html
<div>跨域测试</div>
JSONP
原理
利用script标签的src不受浏览器同源策略约束的特性,发送跨域请求
在test.html的</body> 标签上 添加一个script标签
<div>跨域测试</div>
<script>
function cb() {
console.log(arguments)
}
</script>
<script src="http://127.0.0.1:8887?callback=cb"></script>
server2.js
const http = require('http')
const queryString = require('querystring')
const url = require('url')
const app = http.createServer((req, res) => {
const qs = req.url.substring(req.url.lastIndexOf('?') + 1)
const cb = queryString.parse(qs)
const fn = cb.callback
const callback = fn + '(' + '123' + ')'
res.end(callback)
})
app.listen(8887)
CORS跨域资源共享
XMLHttpRequest和Fetch API遵循同源策略,这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非使用CORS头文件
注意:跨域请求可以正常发起,服务器也会接收并返回数据,但是浏览器会将返回的数据拦截
原理
通过设置response的响应头 Access-Control-Allow-Origin 来允许特定的访问
test.html
<script>
// 利用 服务器端 response的 响应头 Access-Control-Allow-Origin: '*'
var xhr = new XMLHttpRequest()
xhr.open('GET','http://127.0.0.1:8887/')
xhr.send()
</script>
server2.js
const http = require('http')
const app = http.createServer((req, res) => {
console.log('request2 come', req.url);
res.writeHead(200,{
// * 代表允许任何访问,你也可以写死 http://127.0.0.1:8888
'Access-Control-Allow-Origin': '*',
})
res.end('123')
})
app.listen(8887)
跨域的限制
跨域允许的方法:
- get
- head
- post
跨域允许的Content-Type: - text/plain
- multipart/form-data
- application/x-www-form-urlencoded
请求头显示 - 跨域时有些自定义请求头不被允许
下面我们来测试下,注意html页面中 fetch请求的 method 和 headers
test.html
<script>
fetch('http://127.0.0.1:8887',{
method: 'PUT',
headers: {
'X-Test-Cors': '123'
}
})
</script>
const http = require('http')
const app = http.createServer((req, res) => {
res.writeHead(200,{
'Access-Control-Allow-Origin': '*',
// 'Access-Control-Allow-Headers': 'X-Test-Cors',
// 'Access-Control-Allow-Methods': 'Put,Delete'
})
res.end('123')
})
app.listen(8887)
控制台 network信息
浏览器报错信息
原因是什么呢?
跨域请求时,put方法和自定义请求头X-Test-Cors是不被允许的,会触发
CORS预检请求
CORS预检请求
跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET
以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST
请求),浏览器必须首先使用 OPTIONS
方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。
将server2.js修改一下
const http = require('http')
const app = http.createServer((req, res) => {
res.writeHead(200,{
'Access-Control-Allow-Origin': '*',
//服务器允许的自定义的请求首部字段
'Access-Control-Allow-Headers': 'X-Test-Cors',
//服务器允许的请求方法
'Access-Control-Allow-Methods': 'PUT,DELETE,OPTIONS'
})
res.end('123')
})
app.listen(8887)
方法为options的 预检请求
方法为 put的真实请求
可以看到server2.js中我们设置了
//服务器允许的自定义的请求首部字段
'Access-Control-Allow-Headers': 'X-Test-Cors',
//服务器允许的请求方法
'Access-Control-Allow-Methods': 'PUT,DELETE,OPTIONS'
Access-Control-Allow-Headers: 能够使自定义请求头 通过预检
Access-Control-Allow-Methods:能够使指定方法 通过预检
什么情况下会触发 预检请求
“需预检的请求”要求必须首先使用 OPTIONS
方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响
当请求满足下述任一条件时,即应首先发送预检请求:
- 使用了下面任一 HTTP 方法:
- 人为设置了对 CORS 安全的首部字段集合之外的其他首部字段。该集合为:
Accept
Accept-Language
Content-Language
-
Content-Type
(but note the additional requirements below) [DPR](http://httpwg.org/http-extensions/client-hints.html#dpr)
[Downlink](http://httpwg.org/http-extensions/client-hints.html#downlink)
[Save-Data](http://httpwg.org/http-extensions/client-hints.html#save-data)
[Viewport-Width](http://httpwg.org/http-extensions/client-hints.html#viewport-width)
[Width](http://httpwg.org/http-extensions/client-hints.html#width)
-
Content-Type
的值不属于下列之一:application/x-www-form-urlencoded
multipart/form-data
text/plain
- 请求中的
XMLHttpRequestUpload
对象注册了任意多个事件监听器。 - 请求中使用了
ReadableStream
对象。