JSONP是什么

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,返回 JSjava

  • 方案一:用图片造 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,失败返回 400ajax

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

  • 方案二:用 scriptget 请求

这种方法必定要把 script 放到页面里面,浏览器才会发起请求。怎么样才能知道是请求成功仍是失败了呢,固然 script 也有 onloadonerror 事件。后端

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 ,而后 scriptsrc 就是要请求的路径。而后把 script 放到页面里,这样浏览器就是去发起一个这样路径的 GET 请求(没办法 POST )。若是这个请求成功了,那么它首先会执行那个服务器返回的 javascript 响应,这个响应就是操做页面的局部刷新。

这种技术就叫作 SRJ - Server Rendered JavaScript,服务器返回的 JavaScript。这个就是在 AJAX 出现以前用的无刷新局部更新页面内容。

上面的这个 SRJ 方案,若是没有作任何的安全措施的话,任何一个网站均可以去请求这个 API 操做付款,因此像付款这些重要的操做要使要 POSTGET 太容易伪造了。

JSONP

上面都是都一个网站的前端和后端交流,那若是这个网站的前端想和另外一个域名下的接口交流怎么办呢?

前端给后端一个函数,而后后端调用这个函数,要执行的代码不要管,而后返回一个结果。那后台怎么知道这个函数名呢,咱们能够在请求的时候传参。

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()
}
复制代码

给什么就调什么,这样就彻底耦合了, 这就叫作 JSONPJSONP 要结决的一个问题就是两个网站之间怎么交流,咱们用一个 script 标签就能够交流了, script 标签是不受域名限制的。既然不受限制,就能够告诉对方网站我要请求一个数据,对方给数据以后再调用咱们准备的函数,把参数传到函数的第一个参数里面,咱们就能够获得了。

请求方:frank.com 的前端程序员(浏览器)

响应方:jack.com 的后端程序员(服务器)

  1. 请求方建立 scriptsrc 指向响应方,同时传一个查询参数 ?callback=yyy

    yyy 通常是要随机数,这样就不用出现函数名重复的问题。

  2. 响应方根据查询参数 callback,构造形如

    1. yyy.call(undefined, '你要的数据')
    2. yyy('你要的数据') 这样的响应
  3. 浏览器接收到响应,就会执行 yyy.call(undefined, '你要的数据')

  4. 那么请求方就知道了他要的数据

这就是 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 请求。

相关文章
相关标签/搜索