怎么解决跨域问题?
官方解析
跨域问题(Cross-Origin Resource Sharing,CORS)是由于浏览器的同源策略(Same-Origin Policy)导致的。同源策略指的是,如果两个 URL 的协议、主机名和端口号都相同,那么它们就是同源的,否则就是跨域的。当网页发起跨域请求时,浏览器会根据同源策略限制请求。解决跨域问题的方法有以下几种:
JSONP(JSON with Padding)
JSONP 是一种跨域请求数据的方式,它利用了 <script> 标签不受同源策略限制的特性,可以从不同的域名请求数据。实现原理是在服务端生成一个 JavaScript 函数,客户端使用 <script> 标签请求该函数,服务端返回该函数的调用,并将需要传输的数据作为函数参数传入。
CORS(Cross-Origin Resource Sharing)
CORS 是一种通过添加一些 HTTP 头来允许浏览器跨域访问资源的机制,主要是服务端配置。服务端需要在响应头中添加 Access-Control-Allow-Origin 和其他一些参数,指示允许哪些域名进行跨域请求。
反向代理(Reverse Proxy)
反向代理是将客户端的请求转发到真正的服务端,从而解决跨域问题。反向代理服务器和真正的服务端在同一个域名下,客户端的请求只需要向反向代理服务器发起,由反向代理服务器将请求转发到真正的服务端,最后将响应返回给客户端。
WebSocket
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,可以用于跨域通信。由于 WebSocket 协议并不受同源策略的限制,因此可以实现跨域通信。
总的来说,不同的解决方案有各自的优缺点,应根据实际情况选择最适合的方式。例如,如果需要在客户端和服务端之间进行实时通信,WebSocket 是最佳选择;如果只需要在客户端发起简单的 GET 请求,可以使用 JSONP 等方式。
其他回答一
解决跨域的方法有以下几种:
1.jsonp
利用script标签不受浏览器跨域的影响,前端通过src属性将回调函数和请求参数拼接在请求url地址后面并发送给后端,后端通过拼接请求数据和回调函数一并返回给前端,前端通过执行回调函数拿到请求数据。所以jsonp解决的跨域问题具有很明显的不足,它只能实现get方法的请求。对于post put delete 等其它请求无法实现。 具体代码实现:
// 前端
<script src="https://xxx.com/getUserInfo?id=212&callback=getUserInfoRes"></script>
const getUserInfoRes = ({userName}) => {
console.log(userName)
}
// 后端 这里用 express 来模拟
app.get('/getUserInfo', (req, res) => {
const {id, callback} = req.query
const res = db.query('...', id, (err, res) => {
res.send(`callback(res.data)`)
})
})
2.Nginx代理实现
{
listen 80
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Headers X-Requested-With;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS
location / {
proxy_pass http://www.domain.com:8080;
}
}
3.CORS 服务器配置
配置Access-Control-Allow-Origin 为*(所有源)或允许访问的域名/ip地址,Access-Control-Allow-Method 配置请求方法,这是最为常见解决跨域的方法。
4.WebSocket 协议通讯
利用 HTML5 提供的新协议,实现浏览器与服务器的全双工通信,从而解决浏览器的跨域问题。
具体实现:
// 前端
const socket = new WebSocket('http://www.domain.com:8080')
socket.addEventListener('open', function () {
socket.send('...')
})
socket.addEventListener('message', function(e) {
console.log(e.data)
})
// 后端 nodejs
const WebSocket = require('ws')
const server = new WebSocket.Server({post: 8080})
server.addEventListener('connection', function(socket) {
socket.addEventListener('message', function(res) {
socket.send(res)
})
})
其他回答二
跨域问题(Cross-Origin Resource Sharing,CORS)是由于浏览器的同源策略(Same-Origin Policy)导致的。同源策略指的是,如果两个 URL 的协议、主机名和端口号都相同,那么它们就是同源的,否则就是跨域的。当网页发起跨域请求时,浏览器会根据同源策略限制请求。解决跨域问题的方法有以下几种:
1.JSONP
因为浏览器同源策略的存在,导致存在跨域问题,以下这三个标签加载资源路径是不受束缚的
1. script标签:<script src="加载资源路径"></script>
2. link标签:<link herf="加载资源路径"></link>
3. img标签:<img src="加载资源路径"></img>
而JSONP就是利用了script的src加载不受束缚,从而可以拥有从不同的域拿到数据的能力,但是JSONP需要前端后端配合,才能实现最终的跨域获取数据。
JSONP通俗点说就是:利用script的src去发送请求,将一个方法名callback传给后端,后端拿到这个方法名,将所需数据,通过字符串拼接成新的字符串callback(所需数据),并发送到前端,前端接受到这个字符串之后,就会自动执行方法callback(所需数据)
前端代码
// index.html http://127.0.0.1:5500/index.html
const jsonp = (url, params, cbName) => {
return new Promise((resolve,reject) => {
const script = document.createElement('script')
window[cbName] = (data) => {
resolve(data)
document.body.removeChild(script)
}
params = { ...params, callback:cbName }
const str = Object.keys(params).map(key => `${key}=${params[key]}`)
script.src = `${url}?${str.join('&')}`
document.body.appendChild(script)
})
}
jsonp('http://127.0.0.1:8000',{ name:'mos', age:18},'callback').then(data => {
console.log(data) // mos今年18岁了!!!
})
后端代码
// index.js http://127.0.0.1:8000
const http = require('http');
const urllib = require('url');
const port = 8000;
http.createServer(function(req,res){
const { query } = urllib.parse(req.url,true);
if(query && query.callback){
const { name, age, callback } = query
const person = `${name}今年${age}岁了!!!`
const str = `${callback}(${JSON.stringify(person)})` //拼成callback(data)
res.end(str)
}else{
res.end(JSON.stringify('没东西啊你'));
}
}).listen(port,function(){
console.log('server is listening on port ' + port);
})
JSONP的缺点就是,需要前后端配合,并且只支持get请求方法
2.WebSocket
WebSocket根本不附属于同源策略,而且它本身就有意被设计成可以跨域的一个手段。由于历史原因,跨域检测一直是由浏览器端来做,但是WebSocket出现以后,对于WebSocket的跨域检测工作就交给了服务端,浏览器仍然会带上一个Origin跨域请求头,服务端则根据这个请求头判断此次跨域WebSocket请求是否合法
前端代码
// index.html http://127.0.0.1:5500/index.html
function myWebsocket(url,params){
return new Promise((resolve,reject)=>{
const socket = new WebSocket(url)
socket.onopen = () => {
socket.send(JSON.stringify(params))
}
socket.onmessage = (e) => {
resolve(e.data)
}
})
}
myWebsocket('ws://127.0.0.1:8000',{ name:'mos', age:18 }).then(data=>){
console.log(data) // mos今年18岁了!!!
}
后端代码
// index.js http://127.0.0.1:8000
const Websocket = require('ws');
const port = 8000;
const ws = new Websocket.Server({ port })
ws.on('connection',(obj) =>{
obj.on('message',(data) =>{
data = JSON.parse(data.toString())
const { name, age } = data
obj.send(`${name}今年${age}岁了!!!`)
})
})
3.Cors
Cors,全称Cross-Origin Resource Sharing,意思是跨域资源共享,Cors一般是由后端来开启的,一旦开启,前端就可以跨域访问后端。
前端跨域访问到后端,后端开启Cors,发送Access-Control-Allow-Origin:域名字段到前端(其实不止一个),前端浏览器判断Access-Control-Allow-Origin的域名如果跟前端域名一样,浏览器就不会实行跨域拦截,从而解决跨域问题。
前端代码
// index.html http://127.0.0.1:5500/index.html
// 步骤一:创建异步对象
var ajax = new XMLHttpRequest();
// 步骤二:设置请求的url参数,参数一是请求的类型,参数二是请求的url,可以带参数
ajax.open('get','http://127.0.0.1:8000?name=林三心&age=23');
// 步骤三:发送请求
ajax.send();
// 步骤四:注册事件 onreadystatechange 状态改变就会调用
ajax.onreadystatechang = function(){
if(ajax.readyState == 4 && ajax.status == 200){
// 步骤五 如果能够进到这个判断 说明数据完美的回来了,并且请求的页面是存在的
console.log(ajax.responseText);
}
}
后端代码
// index.js http://127.0.0.1:8000
const http = require('http');
const urllib = require('url');
const port = 8000;
http.createServer(function(req,res){
// 开启Cors
res.writeHead(200,{
// 设置允许跨域的域名,也可设置*允许所有域名
'Access-Control-Allow-Origin': 'http://127.0.0.1.5500',
// 跨域允许的请求方法,也可以设置*允许所有方法
"Access-Control-Allow-Methods": 'DELETE,PUT,POST,GET,OPIIONS',
// 允许的header类型
'Access-Contorl-Allow-Headers': 'Content-Type'
})
const { query : { name, age } } = urllib.parse(req.url, true);
res.end(`${name}今年${age}岁啦!!!`);
}).listen(port,function(){
console.log('server is listening on port ' + port);
})
4.反向代理 Node接口代理
同源策略它只是浏览器的一个策略而已,它是不限制后端的,也就是前端-后端会被同源策略限制,但是后端-后端则不会被限制,所以可以通过Node接口代理,先访问已设置Cors的后端1,再让后端1去访问后端2获取数据到后端1,后端1再把数据传到前端
前端代码
// index.html http://127.0.0.1:5500
//步骤一:创建异步对象
var ajax = new XMLHttpRequest();
//步骤二:设置请求的url参数,参数一是请求的类型,参数二是请求的url,可以带参数,动态的传递参数starName到服务端
ajax.open('get', 'http://127.0.0.1:8888?name=mos&age=18');
//步骤三:发送请求
ajax.send();
//步骤四:注册事件 onreadystatechange 状态改变就会调用
ajax.onreadystatechange = function () {
if (ajax.readyState == 4 && ajax.status == 200) {
//步骤五 如果能够进到这个判断 说明 数据 完美的回来了,并且请求的页面是存在的
console.log(ajax.responseText);//输入相应的内容
}
}
后端代码
// index2.js http://127.0.0.1:8888
const http = require('http');
const urllib = require('url');
const querystring = require('querystring');
const port = 8888;
http.createServer(function (req, res) {
// 开启Cors
res.writeHead(200, {
//设置允许跨域的域名,也可设置*允许所有域名
'Access-Control-Allow-Origin': 'http://127.0.0.1:5500',
//跨域允许的请求方法,也可设置*允许所有方法
"Access-Control-Allow-Methods": "DELETE,PUT,POST,GET,OPTIONS",
//允许的header类型
'Access-Control-Allow-Headers': 'Content-Type'
})
const { query } = urllib.parse(req.url, true);
const { methods = 'GET', headers } = req
const proxyReq = http.request({
host: '127.0.0.1',
port: '8000',
path: `/?${querystring.stringify(query)}`,
methods,
headers
}, proxyRes => {
proxyRes.on('data', chunk => {
console.log(chunk.toString())
res.end(chunk.toString())
})
}).end()
}).listen(port, function () {
console.log('server is listening on port ' + port);
})
后端代码
// index.js http://127.0.0.1:8000
const http = require('http');
const urllib = require('url');
const port = 8000;
http.createServer(function (req, res) {
console.log(888)
const { query: { name, age } } = urllib.parse(req.url, true);
res.end(`${name}今年${age}岁啦!!!`)
}).listen(port, function () {
console.log('server is listening on port ' + port);
})
Nginx
其实Nginx跟Node接口代理是一个道理,只不过Nginx就不需要我们自己去搭建一个中间服务
server{
listen 8888;
server_name 127.0.0.1;
location /{
proxy_pass 127.0.0.1:8000;
}
}
5.postMessage
场景:http://127.0.0.1:5500/index.html 页面中使用了iframe标签内嵌了http://127.0.0.1:5555/index.html 的页面
虽然这两个页面存在于一个页面中,但是需要iframe标签来嵌套才行,这两个页面之间是无法进行通信的,因为他们端口号不同,根据同源策略,他们之间存在跨域问题
那应该怎么办呢?使用postMessage可以使这两个页面进行通信
// http:127.0.0.1:5500/index.html
<body>
<iframe src="http://127.0.0.1:5555/index.html" id="frame"></iframe>
</body>
<script>
document.getElementById('frame').onload = function () {
this.contentWindow.postMessage({ name:'mos', age:18 }, 'http://127.0.0.1:5555')
window.onmessage = function (e) {
console.log(e.data) // mos今年18岁啦!!!
}
}
</script>
// http://127.0.0.1:5555/index.html
<script>
window.onmessage = function (e) {
const { data: { name, age }, origin } = e
e.source.postMessage(`${name}今年${age}岁啦!!!`, origin)
}
</script>