跨域常见的几种解决方案
跨域
非同源策略请求(比较协议、域名、端口号,只要有一个不一样就是跨域):
- 页面的访问地址(Web地址)
- 数据接口的请求地址
情况1:开发时候是跨域的,但是服务器部署的时候是同源的
修改本地 HOST【DNS解析】
核心:骗过浏览器,让浏览器认为是同源,但是本质还是跨域
情况2:开发和上线都是跨域的
- JSONP(不安全,并且只支持GET请求)
其它方案
document.domain + iframe
window.name + iframe
H5 postMessage
- CORS 跨域资源共享
- proxy 跨域代理(目前最常用的)
JSONP 跨域资源请求
利用<script>
或者<link> <img> <iframe>...
不存在域的限制
- 特征:资源访问一定都是GET请求,不可能有POST
全局函数 function fn(result) { ... }
<script src='http://127.0.0.1:8888/user/list?callback=fn'>
一定能发送到服务器(不存在域的限制),把全局函数 fn 名字,当做参数传递给服务器- 服务器接收到这个请求,同时也可以获取 callback 传递的值(fn)
- 准备数据,最后返回给客户端
fn([10,20,30])
- 客户端把函数 fn 执行,把服务器准备的数据作为实参传递给函数的形参
服务器代码
首先看一下服务器代码,这里提供了
jsonpTest
这个资源路径,并返回给客户端 一个字符串(包含执行函数和参数)const express = require(‘express’)
const bodyParser = require(‘body-parser’)
const app = express()const port = 1001
app.listen(port, () => {
console.log(The Web Service Is Listening To The Port: ${ port}
)
})app.get(‘/jsonpTest’, (req, res) => {
let fname = req.query.callback
let data = [10, 20, 30]
res.send(${ fname}(${ JSON.stringify(data)})
)
})
JSONP 简单案例
现在来尝试一下 jsonp 的一个简单案例
封装 JSONP
每次像简单案例那样调用太过麻烦,现在我们想像 Axios 那样调用 jsonp
实现代码如下:
当前还可以实现 Promise 版本,这里就不再实现了
可以参考:面试中如何实现一个高质量的JSONP
;(function () {
const jsonp = function jsonp(config) {config == null ? (config = { }) : null
typeof config !== 'object' ? (config = { }) : null
let { url, params = { }, jsonpName = 'callback', success = Function.prototype } = config
// 自己创建一个全局函数
let f_name = `jsonp${ +new Date()}`
window[f_name] = function (result) {
typeof success === 'function' ? success(result) : null
delete window[f_name]
document.body.removeChild(script)
}
// 处理URL
params = Qs.stringify(params)
if (params) url += `${ url.includes('?') ? '&' : '?'}${ params}`
url += `${ url.includes('?') ? '&' : '?'}${ jsonpName}=${ f_name}`
// 发送请求
let script = document.createElement('script')
script.src = url
// script.onerror = () => {};
document.body.appendChild(script)
}
if (typeof window !== ‘undefined’) {window.jsonp = jsonp
}
})()
其它网站 JSONP 案例
<script>
jsonp({
url: 'https://www.baidu.com/sugrec',
params: {
prod: 'pc',
wd: '百度',
},
jsonpName: 'cb',
success: result => {
console.log(result)
},
})
</script>
CORS 跨域资源共享
在发送真实请求之前,浏览器会先发送一个试探性请求 OPTIONS(目的:测试客户端和服务器之间是否可以正常通信)如果可以正常通信,接下来再发送真实请求信息
服务器代码
Allow-Origin 可以设置的值
- 单一源
*
所有源(但是此时不安全,而且不允许携带资源凭证)
假如你希望有多个源(不是所有源)都可以跨域,这时就需要设置一个白名单
// 白名单
const safeList = [, 'http://127.0.0.1:5500', 'http://127.0.0.1:5501']
app.use((req, res, next) => {
let origin = req.headers.origin || req.headers.referer
origin = origin.replace(/\/$/, '')
if (safeList.includes(origin)) {
res.header('Access-Control-Allow-Origin', origin)
res.header('Access-Control-Allow-Credentials', true)
req.method === 'OPTIONS' ? res.send('Current Services Support Domain Request!') : next()
}
})
app.get('/test', (req, res) => {
res.send('OK')
})
客户端代码
<script> fetch('http://127.0.0.1:1001/test').then(response => response.text()).then(data => console.log(data)) </script>
Proxy 跨域代理
爬虫:自己写一个后台,去爬取别的后台的数据(平台和平台之间没有跨域)
- 后台和后台之间没有跨域限制(服务器一般会做白名单)
- 客户端和服务器才有跨域限制(浏览器的安全性)
使用 webpack devServer
插件
module.exports = {
devServer: {
port: '3000',
compress: true,
open: true,
hot: true,
proxy: {
'/': {
target: 'https://www.jianshu.com',
changeOrigin: true,
},
},
},
}
通过代理爬取简书
通过 pipe 实现请求代理
const request = require(‘request’)app.get(‘/subscriptions/recommended_collections’, (req, res) => { let url = ‘https://www.jianshu.com/asimov‘ + req.url req.pipe(request(url)).pipe(res)})// 注意:这里html文件名必须为index.htmlapp.use(express.static(‘./‘))
前端代码
还没有评论,来说两句吧...