JSONP
是什么Jsonp
(JSON with Padding
) 是 json
的一种"使用模式",可让网页从别的域名(网站)那获取资料,即跨域读取数据。javascript
<h5>您的帐户余额是<span id="amount">&&&amount&&&</span></h5>
<button id="button">付款</button>
复制代码
form
发请求<form action="/pay" method="POST" target="result">
<input type="submit" value="付款">
</form>
<iframe name="result" src="about:black" frameborder="0" height="200"></iframe>
复制代码
后端部分代码:html
if(path === '/'){
let string=fs.readFileSync('./index.html','utf8')
let amount=fs.readFileSync('./db','utf8')
string=string.replace('&&&amount&&&',amount)
response.statusCode = 200
response.setHeader('Content-Type', 'text/html;charset=utf-8')
response.write(string)
response.end()
}else if (path === '/pay') {
let amount = fs.readFileSync('./db', 'utf8')
let number = amount - 1
if(Math.random() > 0.5){ //假设大于0.5就成功
fs.writeFile('./db', number)
response.write('success')
}else{
response.write('fail')
}
response.end()
}
复制代码
form
表单提交以后必定会刷新当前页面,这样用户体验很差,利用 iframe
局部刷新页面,优化用户体验。前端
有没有想过,不返回 HTML
,返回 JS
java
get
请求浏览器有个特色一旦发现你在内存里建立了一个 img
,就会去请求这个 img
,这个方法能够悄无声息的创造一个请求,但有个缺陷没法设置 POST
。那就 发GET
请求吧,总比用 iframe
刷新页面好。程序员
button.addEventListener('click', () => {
let image = document.createElement('img')
image.src = './pay'
image.onload = function(){
alert('付款成功')
amount.innerText = amount.innerText -1
}
image.onerror = function(){
alert('付款失败')
}
})
复制代码
浏览器怎么知道一个图片请求是成功仍是失败呢?状态码说的很清楚,假设若是成功就返回 200
,失败返回 400
ajax
if (path === '/pay') {
let amount = fs.readFileSync('./db', 'utf8')
let number = amount - 1
if(Math.random() > 0.5){
fs.writeFile('./db', number)
response.setHeader('Content-Type', 'image/png')
response.statusCode = 200
response.write(fs.readFileSync('./th.jpg'))
}else{
response.statusCode = 400
response.write('fail')
}
response.end()
}
复制代码
这样就作到了无刷新的也没有用什么特殊的技术,只用了一个小技巧就是建立了一个 img
用它发请求。如今的问题就是没法 POST
,由于浏览器没有给咱们一个接口让咱们 POST
,因此那就只好 GET
了, 这种方法只能知道成功或失败,不能知道更多的数据。json
script
造 get
请求这种方法必定要把 script
放到页面里面,浏览器才会发起请求。怎么样才能知道是请求成功仍是失败了呢,固然 script
也有 onload
和 onerror
事件。后端
button.addEventListener('click', () => {
let script = document.createElement('script')
script.src = '/pay'
document.body.appendChild(script)
script.onload = function(){
alert('付款成功')
amount.innerText = amount.innerText -1
}
script.onerror = function(){
alert('付款失败')
}
}
复制代码
改进以后的好处就是不用返回图片了,返回一个字符串就能够了,这样请求就会快一点了。跨域
点击 button
后,就会去建立一个 script
标签会放在页面的最后方,因为页面中出现了一个 script
,浏览器就会将 /pay
里面的内容执行掉。这样就不须要去监听 onload
事件,就监听 onerror
事件就能够了。 onload
之间由 /pay
里面的内容执行,服务器直接返回了在浏览器执行的一个 js
字符串(代码)。浏览器
if (path === '/pay') {
let amount = fs.readFileSync('./db', 'utf8')
let number = amount - 1
if(Math.random() > 0.5){
fs.writeFile('./db', number)
response.setHeader('Content-Type', 'application/javascript')
response.statusCode = 200
response.write(`
alert("付款成功")
amount.innerText = amount.innerText -1
`)
}else{
response.statusCode = 400
response.write('fail')
}
response.end()
}
复制代码
不过还有一个问题就是没点击一次按钮,页面中都会多出现一个 script
,虽说不会出现 BUG,可是会很难看,因此在请求成功或失败以后再删除它。
button.addEventListener('click', () => {
let script = document.createElement('script')
script.src = '/pay'
document.body.appendChild(script)
script.onload = function(e){
e.currentTarget.remove()
}
script.onerror = function(e){
alert('付款失败')
e.currentTarget.remove()
}
})
复制代码
这就是整个的完整方案,当用户点击一个动做的时候,生成一个 script
,而后 script
的 src
就是要请求的路径。而后把 script
放到页面里,这样浏览器就是去发起一个这样路径的 GET
请求(没办法 POST
)。若是这个请求成功了,那么它首先会执行那个服务器返回的 javascript
响应,这个响应就是操做页面的局部刷新。
这种技术就叫作 SRJ - Server Rendered JavaScript
,服务器返回的 JavaScript
。这个就是在 AJAX
出现以前用的无刷新局部更新页面内容。
上面的这个 SRJ
方案,若是没有作任何的安全措施的话,任何一个网站均可以去请求这个 API
操做付款,因此像付款这些重要的操做要使要 POST
,GET
太容易伪造了。
上面都是都一个网站的前端和后端交流,那若是这个网站的前端想和另外一个域名下的接口交流怎么办呢?
前端给后端一个函数,而后后端调用这个函数,要执行的代码不要管,而后返回一个结果。那后台怎么知道这个函数名呢,咱们能够在请求的时候传参。
window.yyy = function(result) {
if(result === 'success'){
amount.innerText = amount.innerText - 1
}else{
}
}
button.addEventListener("click", () => {
let script = document.createElement("script");
script.src = 'http://jackma.com:8002/pay?callback=yyy'
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')
let number = amount - 1
fs.writeFile('./db', number)
response.setHeader('Content-Type', 'application/javascript')
response.statusCode = 200
response.write(`
${query.callback}.call(undefined, 'success')
`)
response.end()
}
复制代码
给什么就调什么,这样就彻底耦合了, 这就叫作 JSONP
。 JSONP
要结决的一个问题就是两个网站之间怎么交流,咱们用一个 script
标签就能够交流了, script
标签是不受域名限制的。既然不受限制,就能够告诉对方网站我要请求一个数据,对方给数据以后再调用咱们准备的函数,把参数传到函数的第一个参数里面,咱们就能够获得了。
请求方:frank.com
的前端程序员(浏览器)
响应方:jack.com
的后端程序员(服务器)
请求方建立 script
,src
指向响应方,同时传一个查询参数 ?callback=yyy
yyy
通常是要随机数,这样就不用出现函数名重复的问题。
响应方根据查询参数 callback
,构造形如
yyy.call(undefined, '你要的数据')
yyy('你要的数据')
这样的响应浏览器接收到响应,就会执行 yyy.call(undefined, '你要的数据')
那么请求方就知道了他要的数据
这就是 JSONP
jQuery
的写法:
$.ajax({
url: "http://jack.com:8002/pay",
dataType: "jsonp",
success: function( response ) {
if(response === 'success'){
amount.innerText = amount.innerText - 1
}
}
})
复制代码
JSONP
为何不支持 POST
请求
由于
JSONP
是经过动态建立script
实现的,动态建立script
的时候只能用GET
请求,没有办法用POST
请求。