JSONP
什么是 JSONP?
JSONP 利用了具有 src 属性的标签引用资源不受同源策略的影响的特性。通过 <script>
标签向服务器端发送请求时在 URL 中通过参数把一个回调函数的名称告知服务端,服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。
JSONP 的优点是简单,可以跨域,老式浏览器支持好。
-
JSONP 是通过
<script>
标签加载数据的方式去获取数据并且当做 JS 代码来执行。 -
需要提前在页面上声明一个函数,以备后台返回数据后执行。
JSON 与 JSONP 的区别
JSON 是一种数据交换格式,是 JavaScript 的一个子集。
JSONP 是 JSON 的一种“使用模式”,可以让网页从别的网域要数据。
JSONP 为什么不支持 POST ?
因为 JSONP 是通过动态创建 <script>
标签实现的, <script>
标签只能发送 GET 请求,无法发送 POST 请求。
用 form 表单提交请求
用form表单提交请求提交表单后会跳转到新页面,用户体验不好。
当然我们也可以使用 iframe ,不过 iframe 有点过时了。
<h5>您的账户余额是<span id="amount">&&&amount&&&</span></h5>
<form action="/pay" method="post">
<input type="submit" value="付款">
</form>
<h5>您的账户余额是<span id="amount">&&&amount&&&</span></h5>
<form action="/pay" method="post" target="result">
<input type="submit" value="付款">
</form>
<iframe name="result" src="about:blank" frameborder="0" height=200></iframe>
后端代码:
if(path === '/pay'){
var amount = fs.readFileSync('./db','utf8');
var newAmount = amount - 1;
fs.writeFileSync('./db',newAmount);
response.write('success');
response.end();
}
局部刷新
方案一:用图片造 get 请求
button.addEventListener('click', (e)=>{
let image = document.createElement('img')
image.src = '/pay'
image.onload = function(){
alert('成功')
}
image.onerror = function(){
alert('失败')
}
})
方案二:用 script 造 get 请求
button.addEventListener('click', (e)=>{
let script = document.createElement('script')
script.src = '/pay'
document.body.appendChild(script)
script.onload = function(e){
e.currentTarget.remove()
}
script.onerror = function(e){
e.currentTarget.remove()
}
})
//后端代码
...
if (path === '/pay'){
let amount = fs.readFileSync('./db', 'utf8')
amount -= 1
fs.writeFileSync('./db', amount)
response.setHeader('Content-Type', 'application/javascript')
response.write('amount.innerText = ' + amount)
response.end()
}
...
JSONP
方案3:JSONP
请求方:frank.com 的前端程序员(浏览器)
响应方:jack.com 的后端程序员(服务器)
请求方创建 script,src 指向响应方,同时传一个查询参数 ?callback=xxx
响应方根据查询参数callback,构造形如
xxx.call(undefined, '你要的数据')
xxx('你要的数据')
这样的响应
浏览器接收到响应,就会执行 xxx.call(undefined, '你要的数据')
那么请求方就知道了他要的数据
这就是 JSONP
读者可以自行对比以下两个 URL 的所返回的内容有何不同:
http://api.douban.com/v2/movie/in_theaters
http://api.douban.com/v2/movie/in_theaters?callback=helloWorld
button.addEventListener('click', (e)=>{
let script = document.createElement('script')
let functionName = 'frank'+ parseInt(Math.random()*10000000 ,10)
window[functionName] = function(){ // 每次请求之前搞出一个随机的函数
amount.innerText = amount.innerText - 0 - 1
}
script.src = '/pay?callback=' + functionName
document.body.appendChild(script)
script.onload = function(e){
e.currentTarget.remove()
delete window[functionName] // 请求完了就干掉这个随机函数
}
script.onerror = function(e){
e.currentTarget.remove()
delete window[functionName] // 请求完了就干掉这个随机函数
}
})
//后端代码
...
if (path === '/pay'){
let amount = fs.readFileSync('./db', 'utf8')
amount -= 1
fs.writeFileSync('./db', amount)
let callbackName = query.callback
response.setHeader('Content-Type', 'application/javascript')
response.write(`
${callbackName}.call(undefined, 'success')
`)
response.end()
}
...
用 jQuery 实现 JSONP
$.ajax({
url: "http://jack.com:8002/pay",
dataType: "jsonp",
success: function( response ) {
if(response === 'success'){
amount.innerText = amount.innerText - 1
}
}
})
$.jsonp()
注意:JSONP 和 Ajax 没有关系。
用代码实现 frank.com:8001 和 jack.com:8002 之间的 JSONP 请求
frank.com:8001 的前端代码:
https://github.com/mokunshao/JSONP-EXAMPLE/blob/master/index.html
jack.com:8002 的后端代码:
https://github.com/mokunshao/JSONP-EXAMPLE/blob/master/server.js#L29