解决跨域
跨域:页面地址和页面请求的接口地址中协议、域名、端口号有一样不同,就说明跨域了,此时,请求会报错
发生跨域的场景:前后端分离式开发、调用第三方接口
解决跨域的4种方案:
CORS中间件
全称:Cross-Origin Resource Sharing 跨域资源共享
是由一系列HTTP头部组成的系统,http头部可以阻止或者允许前端跨域请求的响应
最流行的跨域解决方案
同源策略默认阻止跨域获取资源,但CORS可以选择性的允许或阻止跨域获取资源
OCRS头部:
- Access-Control-Allow-Origin 指定请求的资源可以共享给哪些域
- Access-Control-Allow-Credentials 允许跨域请求的凭证,值为true或false
- Access-Control-Allow-Headers 在预请求中列出正式请求中可以使用哪些请求头
- Access-Control-Allow-Methods 指定允许前端跨域请求接口时使用哪些请求方法(get\post…)
- Access-Control-Expose-Headers 指定可以在客户端浏览器中显示的HTTP头部
- Access-Control-Max-Age 指定预请求的结果可以缓存多长时间,值为整数,单位秒
- Access-Control-Resquest-Headers 在预请求中告知服务器正式请求中会使用哪些请求头
- Access-Control-Request-Method 在预请求中告知服务器正式请求会使用哪些请求方法
- Origin 指示是谁(协议、域名、端口号)发起跨域请求
设置请求头的方式:
res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader("Access-Control-Allow-Methods", "GET, PUT, OPTIONS, POST");
nginx反向代理
- 将请求发送给代理服务器,静态页面和代理服务器之间是同源的,然后代理服务器项后端服务器发请求,服务器和服务器之间不存在跨域
JSONP
原理:script标签可以跨域请求资源
缺点:只能默认发起get请求
需要了解的知识点:
- html中script标签的type属性默认为text/javascript,跨域请求的内容会被当成js代码被执行
- script标签允许跨域请求资源
最简单的jsonp实现:
- 在客户端中创建一个与服务端名称对应的全局函数
- 在script标签中的src属性中引用服务端文件调用该函数,就可以获取该函数中的参数
- 整个过程就是一个函数函数和参数传递过程
// 客户端 client.html 文件: <body> <script> window.getData = function (data) { console.log('data', data) // data {name: 'yangzhiling', age: 99} } </script> <script src="http://127.0.0.1:8001/1_jsonp/server.js"></script> </body>
// 服务端 server.js 文件: getData({ name: 'yangzhiling', age: 99 })
实际中,服务端和客户端之间的数据传递要使用json格式的数据。jsonp的完整使用过程:
- 客户端在script标签的src属性中跨域请求资源的url地址中使用查询字符串拼接要请求数据对应的函数
- 客户端创建一个与请求资源函数对应的函数,该函数获取的参数即我们想要请求的数据
- 服务端将包含真实数据的的整个函数转化为json格式发送给客户端
- 客户端得到这个json格式的函数后,执行该函数,获取数据。由于经常使用js函数包裹json数据,所以这个过程被称为json with padding,简称jsonp
自定义jsonp函数:
- 创建script标签
- 将请求的url地址和想要请求的函数拼接到script标签的src属性中
- 每次请求前清空上一次的请求
<!-- 自定义jsonp函数 --> <script> function jsonp(url, callback) { const cb = '__jp0' const script = document.createElement('script') script.src = `${url}?callback=${cb}` document.body.appendChild(script) window[cb] = function (data) { clear() callback(data) } function clear() { document.body.removeChild(script) window[cb] = null } } jsonp('http://127.0.0.1:8002/jsonp', function (data) { console.log(data) // {name: 'yangzhiling', age: 99} }) </script>
// 自定义服务器 server.js const express = require('express') const db = require('./db.json') const router = require('./router.js') const app = express() app.use(router) app.listen(8002, () => { console.log('express server running at http://127.0.0.1:8002') }) // 自定义路由 router.js const express = require('express') const db = require('./db.json') const router = express.Router() router.get('/jsonp', (req, res) => { const cb = req.query.callback ?? 'cb' res.send(`${cb}(${JSON.stringify(db.messages)})`) }) module.exports = router // 自定义json数据 db.json { "messages": { "name" : "yangzhiling", "age" : 99 } }
postmessage()
- 跨域通信