带你用 Node 了解 JSONP 实现原理
1、JSONP 是什么,解决了什么问题
JSONP 指的是 JSON with Padding。
由于跨域政策,从另一个域请求文件会引起问题。
从另一个域请求外部脚本没有这个问题。
JSONP 利用了这个优势,并使用 script 标签替代 XMLHttpRequest 对象。——w3c
Web 页面上调用 js 文件时不受是否跨域的影响(不仅如此,我们还发现凡是拥有src
这个属性的标签都拥有跨域的能力,比如 、
2、如何实现 JSONP
了解了 JSONP 是什么,我们就来模拟一下一个 JSONP 请求。JSONP 的实现方式一般是在请求参数里添加"callback=函数名"的方式,这里我们使用 nodejs 作为服务。
首先搭建一个本地服务,这里使用 express,先全局安装一下 express
npm install express -g
然后创建一个文件夹,进入文件夹目录下执行下面命令
npm init
这里一路回车就可以
安装 express
npm install express -s
这个时候我们的目录是这样的
├── node_modules
├── package-lock.json
└── package.json
在根目录下新建 index.js 文件
├── index.js
├── node_modules
├── package-lock.json
└── package.json
文件内容如下
var express = require('express')
//2. 创建express服务器
var server = express()
//3. 访问服务器(get或者post)
//参数一: 请求根路径
//3.1 get请求
server.get('/', function(request, response) {
// console.log(request)
response.send({ name: 'starfishing' })
})
server.get('/json/getUser', function(request, response) {
console.log(request.query)
let callback = request.query.callback
// console.log(request)
let str = callback + "({'name':'starfishing','age':22})"
response.send(str)
})
//4. 绑定端口
server.listen(4040)
console.log('启动4040')
到这里我们的服务就搭好了,执行 node index.js 启动服务
然后新建一个 html 页面,内容如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>JSONP测试</title>
</head>
<body>
<script>
function jsonp(data) {
// 这是我们的回调函数
console.log(data.name, data.age)
}
</script>
<script src="http://localhost:4040/json/getUser/?callback=jsonp"></script>
</body>
</html>
在浏览器打开页面,调出控制台,可以看到我们回调函数打印出的内容
3、jsonp 进阶,使用 promise 实现 jsonp
我们会基于 promise 封装一个 JSONP 函数,包括请求超时逻辑,下面贴一下代码
function JSONP(url, params, overtimes) {
return new Promise(function(resolve, reject) {
let query = []
//注册回调函数
let callbackName = 'jsonp' + Math.ceil(Math.random() * 1000000)
window[callbackName] = function(json) {
head.removeChild(script) //移除scipt标签
script.timer ? clearTimeout(script.timer) : false //清除超时计时器
window[callbackName] = null
resolve(json) //成功处理
}
for (let key in params) {
query.push(key + '=' + params[key])
}
query.push('callback=' + callbackName)
let remote = url + '/?' + query.join('&')
let script = document.createElement('script')
script.src = remote
let head = document.getElementsByTagName('head')[0]
head.appendChild(script)
script.onerror = function(err) {
if (window && window[callbackName]) {
delete window[callbackName]
}
try {
head.removeChild(script)
} catch {}
reject(err)
}
if (overtimes) {
script.timer = setTimeout(function() {
if (window && window[callbackName]) {
delete window[callbackName]
}
try {
head.removeChild(script)
} catch {}
reject({ timer: 'overtime' })
}, overtimes)
}
})
}
然后对我们的 node 服务做一下改造来验证我们的函数
var express = require('express')
//2. 创建express服务器
var server = express()
//3. 访问服务器(get或者post)
//参数一: 请求根路径
//3.1 get请求
server.get('/', function(request, response) {
// console.log(request)
response.send({ name: 'xiaoxin' })
})
server.get('/json/getUser', function(request, response) {
console.log(request.query)
let callback = request.query.callback
let str = callback + "({'name':'starfishing','age':22})"
// 下面是验证正常返回的情况
response.send(str)
// 下面是验证服务器错误的情况
// response.status(500)
// 下面验证超时
// setTimeout(function() {
// response.send(str)
// }, 6000)
})
//4. 绑定端口
server.listen(4040)
console.log('启动4040')
最后把新的函数放到 HTML 文件中进行验证
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>JSONP测试</title>
</head>
<body>
<script>
function JSONP(url, params, overtimes) {
// 这里就是我们上面封装的函数,直接拷贝到这里,避免篇幅太长,这里就不拷进来了
}
</script>
<script>
JSONP('http://localhost:4040/json/getUser', { name: 'haha' }, 5000)
.then(data => {
console.log(data)
})
.catch(err => {
console.log(err)
})
</script>
</body>
</html>
← 将嵌套数组转换为一维数组 实现观察者模式 →