同源:如果两个页面有相同的协议、域名和端口,那么这两个页面就属于同一个源,其中只要有一个不相同,就是不同源。不同源的网站之间是不允许发送ajax请求的
解决方案:
1.JSONP解决同源限制问题,其整体原理如下(若想直接用可直接跳转到优化4):
假设有两个服务器,服务器 s1监听3000端口,s2监听3001端口
// s1服务器对应的前端代码
<script>
function fn (data) {
console.log('3000服务器的客户端被执行了')
console.log(data)
}
</script>
// 函数定义要写在前面 不需要在这里调用函数 等另一个服务器返回函数调用
<script src="http://localhost:3001/test"></script>
// s2服务器对应的服务器端代码
app.get('/test', (req, res) => {
var result = 'fn({username: "zhangsan"})'
res.send(result) // 将函数调用传递过去
})
优化1:将客户端定义的函数名字通过get参数的方式传递给服务器端 并在服务器端采用字符串拼接的方式进行传递
// s1服务器对应的客户端代码
<script>
function fn (data) {
console.log('3000服务器的客户端被执行了')
console.log(data)
}
</script> // 函数定义要写在前面 不需要在这里调用函数 等另一个服务器返回函数调用
<script src="http://localhost:3001/better?callback=fn"></script>
// s2服务器对应的服务器端代码
app.get('/better', (req, res) => {
var funName = req.query.callback // 获取客户端传递过来的get参数
var result = funName + '({username: "zhangsan"})'
res.send(result) // 将函数调用传递过去
})
优化2:当点击按钮时再发送请求
// s1服务器对应的前端代码
<button id="btn">点击</button>
<script>
function fn (data) {
console.log('3000服务器的客户端被执行了')
console.log(data)
}
</script> // 函数定义要写在前面 不需要在这里调用函数 等另一个服务器返回函数调用
<script type="text/javascript">
var btn = document.getElementById('btn')
btn.onload = function () {
var script = document.createElement('script)
script.src = "http://localhost:3001/better?callback=fn"
document.body.appendChild(script)
script.onload = function () {
document.body.removeChild(script)
}
}
</script>
// <script src="http://localhost:3001/better?callback=fn"></script>
// s2服务器对应的服务器端代码
app.get('/better', (req, res) => {
var funName = req.query.callback // 获取客户端传递过来的get参数
var result = funName + '({username: "zhangsan"})'
res.send(result) // 将函数调用传递过去
})
优化3:封装jsonp方法
// s1服务器对应的前端代码
<button id="btn">点击</button>
<script>
function fn (data) {
console.log('3000服务器的客户端被执行了')
console.log(data)
}
</script> // 函数定义要写在前面 不需要在这里调用函数 等另一个服务器返回函数调用
<script type="text/javascript">
var btn = document.getElementById('btn')
btn.onload = function () {
jsonp({
url: "http://localhost:3001/better?callback=fn"
})
}
function jsonp (options) {
var script = document.createElement('script)
script.src = options.url
document.body.appendChild(script)
script.onload = function () {
document.body.removeChild(script)
}
}
</script>
// s2服务器对应的服务器端代码
app.get('/better', (req, res) => {
var funName = req.query.callback // 获取客户端传递过来的get参数
var result = funName + '({username: "zhangsan"})'
res.send(result) // 将函数调用传递过去
})
优化4:将处理函数与jsonp函数进行关联 同时自动生成函数名 支持传递多个参数
// s1服务器对应的前端代码
<button id="btn">点击</button>
<script type="text/javascript">
var btn = document.getElementById('btn')
btn.onload = function () {
jsonp({
url: "http://localhost:3001/better",
data: {
username: 'lisi',
age: 20
},
success: function (data) {
console.log('3000服务器的客户端被执行了')
console.log(data)
}
})
}
function jsonp (options) {
var script = document.createElement('script)
// 拼接字符串
var params = ''
for (var attr in options.data) {
params += '&' + attr + '=' + options.data[attr]
}
var funName = 'myJsonp' + Math.random().toString().replace('.', '') // 函数名不能是纯数字
// options.success 客户端定义的函数应该是全局函数 所以将其挂载到window下
// window.fn1 = options.success // 将函数变成全局函数 此时函数名固定 为fn1
// script.src = options.url + '?callback=fn1'
// 将函数名变成随机生成的 避免重复 如果重复 后调用的值会覆盖之前调用返回的值
window[funName] = options.success // 将函数变成全局函数 变量不能用'.' 用'[]'调用或添加
script.src = options.url + '?callback=' + funName + params
document.body.appendChild(script)
script.onload = function () {
document.body.removeChild(script)
}
}
</script>
// s2服务器对应的服务器端代码
app.get('/better', (req, res) => {
var funName = req.query.callback // 获取客户端传递过来的get参数
var result = funName + '({username: "zhangsan"})' // 传递给send的是一个字符串
res.send(result) // 将函数调用传递过去
})
优化4中还可对s2服务器对应的服务器端代码进行优化,总代码如下:
// s1服务器对应的前端代码
<button id="btn">点击</button>
<script type="text/javascript">
var btn = document.getElementById('btn')
btn.onload = function () {
jsonp({
url: "http://localhost:3001/better",
data: {
username: 'lisi',
age: 20
},
success: function (data) {
console.log('3000服务器的客户端被执行了')
console.log(data)
}
})
}
function jsonp (options) {
var script = document.createElement('script)
// 拼接字符串
var params = ''
for (var attr in options.data) {
params += '&' + attr + '=' + options.data[attr]
}
var funName = 'myJsonp' + Math.random().toString().replace('.', '') // 函数名不能是纯数字
// options.success 客户端定义的函数应该是全局函数 所以将其挂载到window下
// window.fn1 = options.success // 将函数变成全局函数 此时函数名固定 为fn1
// script.src = options.url + '?callback=fn1'
// 将函数名变成随机生成的 避免重复 如果重复 后调用的值会覆盖之前调用返回的值
window[funName] = options.success // 将函数变成全局函数 变量不能用'.' 用'[]'调用或添加
script.src = options.url + '?callback=' + funName + params
document.body.appendChild(script)
script.onload = function () {
document.body.removeChild(script)
}
}
</script>
// s2服务器对应的服务器端代码
app.get('/better', (req, res) => {
const funName = req.query.callback // 获取客户端传递过来的get参数
const data = JSON.stringify({username: "zhangsan"})
const result = funName + '(' + data + ')' // 传递给send的是一个字符串
res.send(result) // 将函数调用传递过去 接收字符串 此时传递过去的是函数调用 客户端接收时参数就是对象格式就不需要再用JSON.parse()进行转换了
// 如果是单纯传递一个字符串 在客户端调用时需要先将字符串转成json格式
})
2.CORS实现跨域资源共享,它允许浏览器向跨域服务器发送Ajax请求 克服了Ajax只能同源使用的限制
其基本实现原理为:
s1服务器对应的客户端正常向s2服务器发起ajax请求,s2服务器端设置请求头信息即可实现跨域请求
// s2服务器对应的服务器端代码
app.use((req, res, next) => {
// 1.允许哪些客户端访问我
// *代表允许所有的客户端访问我
res.header('Access-Control-Allow-Origin', '*')
// 2.允许客户端使用哪种请求方式访问我
res.header('Access-Control-Allow-Methods', 'get, post')
next()
})
3.访问非同源数据 服务器端解决方案
同源政策是浏览器给予Ajax技术的限制,服务器端是不存在同源政策的限制,s1网站的客户端向s1服务器端发送数据请求,然后s1服务器向s2服务器发送数据请求,最后s1服务器将获取到的数据再响应给s1网站的客户端。
1.s1网站的客户端正常向s1服务器端发送ajax请求,并在s1服务器端安装request模块,命令为: npm install request
s1网站的服务器端:
const request = require('request')
app.get('/server', (req, res) => {
request('http://localhost:3001/cross', (err, response, body) => {
res.send(body) // 向http://localhost:3001/cross发送请求 body为s2服务器端返回的数据
})
})
s2网站的服务器端:
app.get('/cross', (req, res) => {
res.send('ok')
})