7.跨域请求
三种跨域方案
:
- JSONP模拟ajax请求
- CORS跨域请求
- 让服务端作为客户端的代理进行请求
ajax请求限制
Ajax只能自己的服务器发送请求,也就是只能发送同源请求(判断两个请求是否同源:相同http/https协议,域名,端口),比如现在有A和B两个网站,A网站中的HTML文件只能向A服务器获取数据,B网站…获取数据,A只能向A服务器发送Ajax请求,而A如果向B发送请求,能发送,但是浏览器拒绝接收数据
同源规定
A网站只能向A网站服务器发送http/https请求,浏览器会判断请求是否同源来接收或者拒绝响应,也就是ajax是否能进行跨域请求,同源政策是为了保证用户信息而出现的,A网站在客户端设置的cookie,B网站不能进行访问!!!
同源的标准:
- 协议是否相同(http 和 https)
- 域名是否相同(www.baidu.com 和 www.tenct.com)
- 端口是否相同(Http://www.baidu.com:3000 和 Http://www.baidu.com:3001)
只要以上一项不满足,就是属于非同源的数据,浏览器会拒绝接受响应
1. JSONP模拟请求
JSONP是 json with padding 的缩写,他不属于Ajax请求,但是可以模拟ajax请求(使用script标签),需要前后端配合才能完成jsonp的请求(所有jsonp请求都是get请求)
-
将不同源的服务器请求地址写在script标签的src标签中(请求地址必须返回js代码)
<script src="www.baidu.com"></script> <script src="www.aaa.com/file/index.js"></script> /** * script标签的src属性不受同源政策的限制,但是请求地址返回的数据必须是js代码,并且会直接 * 执行,所有jsonp请求都是get请求 */
-
服务端响应的数据必须是一个函数的调用,真正要发给客户端的数据作为函数的参数
app.get('/jsonp', (req, res) => { const data = { name: 'xyb', age: 21 } const str = `fn(${JSON.stringify(data)})`; // => "fn({"name": "xyb","age": 21})" res.send(str) })
-
在客户端全局作用于下定义函数fn,并在函数内部对数据进行处理
function fn(data) { console.log(data) }
封装JSONP函数
优化的步骤
-
优化一:主动传递函数名,让服务器返回带有函数名的函数执行
客户端:
<script src="http://127.0.0.1:4000/jsonp?callback=fn"></script>
服务端:
app.get('/jsonp', (req, res) => { const data = { name: 'xyb', age: 21 } let callback = req.query.callback; // 获取服务端传过来的参数 const str = `${callback}(${JSON.stringify(data)})`; // fn({"name":...}) res.send(str) })
-
优化二:将script请求的发送变成动态发送
客户端
<button>按钮</button> <script> function fn(data) { console.log(data) } let btn = document.querySelector('button'); btn.addEventListener('click', function() { // 1. 点击按钮,动态创建script标签 let script = document.createElement('script'); // 2. 给上src属性 script.src = "http://127.0.0.1:4000/jsonp?callback=fn"; // 3. 网页面中添加标签,模拟ajax请求 document.body.appendChild(script) // 4. script标签加载完成,删除标签 script.addEventListener('load', function() { // onload => script标签加载完成调用 document.body.removeChild(script) }) }) </script>
-
优化三:封装jsonp函数,方便发起多次请求(最终版本)
客户端:
btn1.addEventListener('click', function () { jsonp({ url: 'http://127.0.0.1:4000/jsonp', success: function(data) { console.log(112233) console.log(data) } }) }) btn2.addEventListener('click', function () { jsonp({ url: 'http://127.0.0.1:4000/jsonp', query: { name: 'xyb', age: 20 }, success: function(data) { console.log(444555666) console.log(data) } }) }) function jsonp(option) { // 1. 创造script标签 let script = document.createElement('script'); // 2. 创建处理数据的函数名,不能重复(时间戳),绑定到window上 let myFunction = 'myFunction' + (+new Date()).toString(); window[myFunction] = option.success // 3. 如果有参数传入,拼接参数 let queryStr = ""; for(let k in option.query) { queryStr += ('&' + k + '=' + option.query[k]) } // 4. 给script添加请求地址,拼接其他参数 script.src = option.url + '?callback=' + myFunction + queryStr; // 5. 添加script标签,执行请求 document.body.appendChild(script) // 6. 加载完成,删除script函数 script.addEventListener('load', function() { document.body.removeChild(script) }) }
服务端:
app.get('/jsonp', (req, res) => { // const data = { // name: 'xyb', // age: 21 // } // let callback = req.query.callback; // const str = `${callback}(${JSON.stringify(data)})`; // res.send(str) // 服务端的jsonp方法,会执行上面的步骤 res.jsonp({ name: 'xyb', age: 20 }) })
2. CORS跨域请求
CORS:全称为Cross-Origin-Resource-Sharing,即为资源共享,它允许浏览器向服务器发送ajax请求,克服了浏览器的同源政策,客户端代码不需要改动,只需要服务器进行配置即可
浏览器发送ajax请求,会带上origin(在请求头中),如果服务器允许当前客户端的请求,在响应头中加入Access-Control-Allow-Origin
,浏览器会查看响应头中有没有该字段,或客户端是否在服务器的白名单中,也就是Access-Control-Allow-Origin
中,若其值为 *
那么代表所有客户端都能跨域请求
步骤:
-
客户端代码(不变):
let xhr = new XMLHttpRequest(); xhr.open('GET', 'http://127.0.0.1:4000/json'); xhr.onreadystatechange=() => { if(xhr.readyState == 4) { if((xhr.status >= 200 && xhr.status <= 300) || xhr.status == 304) { console.log(xhr.responseText); } else { alert('xhr is unsuccessful' + xhr.status) } } } xhr.send(null);
-
服务端代码(加响应头):
// 单独的请求 app.get('/json', (req, res) => { // 代表允许哪些客户端访问 res.header("Access-Control-Allow-Origin", "*") // 代表允许客户端通过哪些方法访问 res.header("Access-Control-Allow-Methods", "get,post") res.send({ "name": "2号", "age": 21, "hobby": "money" }) }) // 设置所有的请求 app.use((req, res, next) => { // 在这里会拦截所有的请求,并添加请求头 // 代表允许哪些客户端访问 res.header("Access-Control-Allow-Origin", "*") // 代表允许客户端通过哪些方法访问 res.header("Access-Control-Allow-Methods", "get,post") // 继续 next() })
3. 服务端进行请求
由于服务器之间没有同源政策的限制,可以让服务器发送请求来获取数据
A服务器向B服务器获取数据再响应给A网站,A服务器充当一个爬虫的角色
步骤:
-
A网站代码:
<script> let xhr = new XMLHttpRequest(); xhr.open('GET', 'http://127.0.0.1:4000/server') xhr.onreadystatechange=() => { if(xhr.readyState == 4) { if((xhr.status >= 200 && xhr.status <= 300) || xhr.status == 304) { console.log(xhr.responseText); } else { alert('xhr is unsuccessful' + xhr.status) } } } xhr.send(null) </script>
-
A服务端代码:
app.get('/server', (req, res) => { request.get('http://127.0.0.1:3000/json', (err, response, body) => { res.send(body) }) })
-
B服务端代码:
app.get('/json', (req, res) => { res.send({ "name": "1号", "age": 21, "hobby": "money" }) })