title: JSONP是什么?
date: 2018-10-06 11:34:12
tags: [JavaScript]
categories: JavaScript
以前是如何发送请求的?
有一些HTML标签能发送请求,比如<form>
标签、<a>
标签、<link>
标签、<script>
标签、<img>
标签。
我们引入一个例子来说明:
用户每点击一下付款按钮,页面和数据库中的余额减少1。
使用form表单
<body>
<h5>你的账户余额为<span id="amount">&&&amount&&&</span></h5>
<form action="/pay" method="post">
<input type="submit" value="付款1块钱">
</form>
</body>
form表单一旦提交就会自动刷新页面,进入pay页面,需要用户退回index页面,刷新页面才能看到结果。
而a标签也会刷新页面或打开新页面。
这样的用户体验很差,程序员就想办法怎么样才能发送请求并局部刷新页面。
使用form + iframe
<body>
<h5>你的账户余额为<span id="amount">&&&amount&&&</span></h5>
<form action="/pay" method="post" target="result">
<input type="submit" value="付款1块钱">
</form>
<iframe src="about:blank" frameborder="0" name="result" height="200"></iframe>
</body>
这种方法发送请求后不会自动跳转页面,但余额部分不会局部刷新,这也是过时的方法了。
动态创建图片
<body>
<h5>你的账户余额为<span id="amount">&&&amount&&&</span></h5>
<button id="button">付款1块钱</button>
<script>
button.addEventListener('click', (e) =>{
let image = document.createElement('img')
image.src = '/pay'
image.onload = function () {
alert('打钱成功')
window.location.reload() //刷新页面
}
image.onerror = function () {
alert('打钱失败')
}
})
</script>
</body>
这样子发送请求虽然能成功(浏览器控制台中返回了200状态码),但实际上仍显示打钱失败,这是为什么呢?
因为后端代码中返回的不是真正的图片,被浏览器识别出来,导致image.onerror
执行,如果使用的是真图片就会执行image.onload
。所以后端必须返回一个图片。
- 另外img标签还有一个弊端是只能发起GET请求不是发起POST请求。
动态创建script
<body>
<h5>你的账户余额为<span id="amount">&&&amount&&&</span></h5>
<button id="button">付款1块钱</button>
<script>
button.addEventListener('click', (e) =>{
let script = document.createElement('script')
script.src = '/pay'
document.body.appendChild(script)
script.onload = function(){
alert('success')
window.location.reload() //刷新页面
}
script.onerror = function(){
alert('fail')
}
})
</script>
</body>
动态创建img不用放入页面就能发起请求,而script必须要放入页面才能发起请求
因为这种方法会在页面中放入一个script,那么里面的内容就会执行,所以我们可以不用监听script.onload,直接在后端返回的script中写入我们要执行的代码。
node.js:
response.write(`alert('success')
window.location.reload()`)
- 页面中会插入script,那么在请求成功后应该删除script (SRJ)
script.onload = function(e){
e.currentTarget.remove()
}
- script实际上依然存在于内存,只是在页面中消失了
原生JS:
button.addEventListener('click',(e)=>{
let script = document.createElement('script');
let functionName = 'allen'+parseInt(Math.random()*100000,10);
window[functionName] = function (result) {
if(result === 'success'){
amount.innerText = amount.innerText - 1
}else {
alert(`fail`)
}
};
script.src = 'http://jack.com:8002/pay?callback=' + functionName;
document.body.appendChild(script);
script.onload = function (e) {
e.currentTarget.remove();
delete window[functionName]
};
script.onerror = function (e) {
alert('fail');
e.currentTarget.remove();
delete window[functionName]
}
});
-
这种方法可以跨域请求。
比如能在xxx.com中请求zzz.com的JS,但因为script请求只能GET,很不安全,隐私相关的重要操作还是需要使用POST请求。
附server代码:
https://github.com/yuyuye958/git-nodejs/blob/master/server.js
所以JSONP到底是什么?
请求方:allen.com的前端程序员(浏览器)
响应方:frank.com的后端程序员(服务器)
- 请求方创建script,src指向响应方,同时传一个查询参数 ?callbackName=xxx
- 响应方根据查询参数callbackName,构造形如
i. xxx.call(undefined, '你要的数据')
ii. xxx('你要的数据')
这样的响应 - 浏览器接收到响应,就会执行xxx.call(undefined, '你要的数据')
- 那么请求方就知道了他要的数据
这就是JSONP
约定:
- callbackName -> callback / jQuery_callback
- xxx -> 随机数 如allen16516541644464()
- jQuery 用法
$.ajax({
url: "http://jack.com:8002/pay",
dataType: "jsonp",
success: function( response ) {
if(response === 'success') {
amount.innerText = amount.innerText - 1
}
}
})
- 注意JSONP不是ajax
JSONP 为什么不支持 POST
- 因为 JSONP 是通过动态创建 script 实现的
- 创建 script 只能发送 get 请求