啥是JSONP?
首先引入一个需求:

用户每点击一下付款按钮,页面和数据库中的余额减少1。
先来理解一下客户端和服务器的交互过程:
- 客户端发起请求
- 服务器接受请求并处理
- 服务器返回数据到客户端
但是,HTML标签中能发请求的标签很少,比如<a>
标签、<form>
标签、<link>
标签、<script>
标签、<img>
标签等。
方案一:使用表单<form>发请求
HTML
<body>
<h5>你的账户余额为<span id="amount">&&&amount&&&</span></h5>
<form action="/pay" method="post">
<input type="submit" value="付款1块钱">
</form>
</body>
向服务器发起请求,请求结果返回到pay页面。
form表单一旦提交就会刷新页面,进入pay页面,需要用户退回index页面,刷新页面才能看到结果。
方案二:使用表iframe
HTML
<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>



以上两种方法是以前的使用方法。
方案三:动态创建图片发请求:
HTML:
<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'
})
</script>
</body>

但是这样用户并不知道请求到底是否成功。因为页面并没有刷新产生任何变化。
修改代码为:
<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>


虽然成功请求到了,但是为社么么会显示打钱失败?
因为后端代码中返回的不是真图片只是图片格式,被浏览器识别出来,导致
image.onerror
执行,如果使用的是真图片就会执行image.onload
。
使用image发请求有一个弊端,只能发起GET请求不是发起POST请求。
方案四:使用script发起请求(JSONP)
<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>
使用image不用放入页面就能发起请求,script必须要放入页面才能发起请求


但是这种方法会在页面中放入一个script,并且里面的内容会执行,所以我们可以不用监听script.onload,直接在后端返回的script中写入我们要执行的代码。
node.js:
response.write(`alert('success')
window.location.reload()`)
但是页面最后会插入script标签,于是请求成功之后删除script(SRJ方案)
script.onload = function(e){
e.currentTarget.remove()
}
但依然存在于内存,只是在页面中消失
JavaScript可以跨域请求。可以在xxx.com的网站中请求zzz.com的JS,但是script请求也是GET,这样容易被伪造,重要操作使用POST请求。
JSONP解决了两个网站跨域交流的问题
原生JS
button.addEventListener('click',(e)=>{
let script = document.createElement('script');
let functionName = 'zink'+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]
}
});
跨域请求
请求方:zink.com的前端程序员(浏览器)
响应方:jack.com的后端程序员(服务器)
1.请求方创建script,src指向响应方,同时传一个查询参数 ?callbackName=xxx
response.write(`
${query.callback}.call(undefined,'success')
`)
JSONP示例:
response.write(`
${query.callback}.call(undefined,{ //左padding
"success": true, //这一部分是JSON
"left": ${newAmount} } //这一部分是JSON
) //右padding
`) //右padding
//JSON + padding = JSONP
这句话是精髓
${query.callback}.call(undefined,'success')
'success'是JSON,左边的叫左padding,右边的括号叫右padding,合起来叫JSONP
这样来调用请求方的callback函数,名字是functionName。
这里才是正经方法
2.服务器通过响应方根据查询参数callbackName,构造形如以下的响应
- functionName.call(undefined, '你要的数据')
- functionName('你要的数据')
3.浏览器接收到响应,就会执行functionName.call(undefined, '你要的数据')
4.那么请求方就知道了他要的数据
这就是JSONP
约定:
- callbackNmae —> callback
- functionName 是一个随机数
jQuery写法:
注意是JSONP不是ajax
$.ajax({
url:"http://jack.com:8002/pay",
dataType: "jsonp",
success:function (response) {
if(response === 'success'){
amount.innerText = amount.innerText-1;
}
}
})
面试题:请问JSONP为什么不支持POST请求?
因为JSONP的本质是动态创建script发起请求,而js动态创建cript只能发起GET请求,所以无法支持POST请求。
网友评论