写一个JSONP

作者: 不爱举铁的伪文青不是好前端 | 来源:发表于2019-03-09 15:25 被阅读19次

首先让我们来先写一个server

var http = require('http')
var fs = require('fs')
var url = require('url')
var port = process.argv[2]

if(!port){
  console.log('请指定端口号好不啦?\nnode server.js 8888 这样不会吗?')
  process.exit(1)
}

var server = http.createServer(function(request, response){
  var parsedUrl = url.parse(request.url, true)
  var pathWithQuery = request.url 
  var queryString = ''
  if(pathWithQuery.indexOf('?') >= 0){ queryString = pathWithQuery.substring(pathWithQuery.indexOf('?')) }
  var path = parsedUrl.pathname
  var query = parsedUrl.query
  var method = request.method

  /******** 从这里开始看,上面不要看 ************/

  console.log('包含查询字符串的路径\n' + pathWithQuery)

  if(path === '/'){
    var string = fs.readFileSync('./index.html','utf8')
    response.setHeader('Content-Type', 'text/html;charset=utf-8')
    response.write(string)
    response.end()
  }else if(path === '/style.css'){
    var string = fs.readFileSync('./style.css','utf8')
    response.setHeader('Content-Type', 'text/html;charset=utf-8')
    response.write(string)
    response.end()
  }else if(path === '/main.js'){
      var string = fs.readFileSync('./main.js','utf8')
      response.setHeader('Content-Type','application/javascript')
      response.write(string)
      response.end()
  }else{
      response.statusCode = 404
      response.setHeader('Content-Type','text/html;charset=utf-8')
      response.write('找不到对应的路径,需要先自行修改')
      response.end()
  }

  /******** 代码结束,下面不要看 ************/
})

server.listen(port)
console.log('监听 ' + port + ' 成功\n请用在空中转体720度然后用电饭煲打开 http://localhost:' + port)

html代码

    <p>您的余额是<span id="amount">&&amount&&</span></p>
    <button id='click'>Click</button>
    <script src="./main.js"></script>

main.js代码

click.addEventListener('click',function(e){
    amount.innerText = amount.innerText - 1
})

返回html会同时返回里面内联的main.js文件和style.css


ASYUJJ.png

当我们点击一下余额数字就会减一,但是如果刷新页面,数字就会重新回到100,所以我需要一个数据库来存放余额


ASYhQI.png
我们给余额添加一个数据库,首先新建一个bd文件,然后把100写进去,然后修改server.js
 if(path === '/'){
    var string = fs.readFileSync('./index.html','utf8')
    var amount = fs.readFileSync('./bd','utf-8') //读取bd文件里面的amount值
    string = string.replace('&&amount&&',amount) //用amount来替换html里面的&&amount&&
    response.setHeader('Content-Type', 'text/html;charset=utf-8')
    response.write(string)
    response.end()
  }
<p>您的余额是<span id="amount">&&amount&&</span></p>

接着我们要告诉服务器,每当我点一下button你都去拿bd里的数字,那么我们就要发一个post请求了,那我们用form来发一个请求吧

  <form action="/pay" method="post">
        <input type="submit" value="付款">
    </form>

然后server处理这个请求

else if (path === "/pay" && method.toUpperCase() === 'POST') {
        var amount = fs.readFileSync('./bd', 'utf-8')
        var newAmount = amount - 1
        if (Math.random() > 0.5) {
            fs.writeFileSync('./bd', newAmount)
            response.write('success')
        } else {
            response.write('fail')
        }
        response.end()
    }

form表单提交,每次点击后success和fail刷新后显示,想要看到余额变化需要返回再刷新,于是我们可以添加一个iframe来承载success和fail,让他在iframe里面刷新,而不是当前页面

   <form action="/pay" method="post" target="result">
        <input type="submit" value="付款">
    </form>
    <iframe src="about:blank" name="result" frameborder="0"></iframe>
ASwkss.png

但是这样体验还是很差,用户仍需要刷新才能看到余额变化,而且还要加一个iframe,那么能不能不用iframe,发请求除了用form还能用什么呢?css的link,a标签,img标签和script标签都可以发请求。
先尝试动态创建一个图片来发请求。
main.js 代码

click.addEventListener('click',function(e){
   let image = document.createElement('img')
   image.src = '/pay'
})

这种方法没用办法去post,一定会去get.而且我们还不知道他成功没成功,我们可以通过image.onload()和image.onerror()方法,并且在方法内部判断返回的状态码来确定成功和失败
main.js代码

click.addEventListener('click',function(e){
   let image = document.createElement('img')
   image.src = '/pay'
   image.onload = function(){
     alert('打钱成功')
     amount.innerText = amount.innerText - 1
   }
   image.onerror = function(){
     alert('打钱失败')
   }
})

server代码

else if (path === "/pay") {
        var amount = fs.readFileSync('./bd', 'utf-8')
        var newAmount = amount - 1
        if (Math.random() > 0.5) {
            fs.writeFileSync('./bd', newAmount)
            response.setHeader('Content-Type','image/jpg')
            response.statusCode =200
            response.write(fs.readFileSync('./dog.jpg'))
            
        } else {
            response.statusCode =400
            response.write('fail')
            
        }
        response.end()
    }

现在实现了无刷新的更新,但是没有办法post。我们再来尝试一下script发请求
main.js代码

click.addEventListener('click',function(e){
  let script = document.createElement('script')
  script.src = '/pay'
  document.body.appendChild(script)
  script.onload = function(){
      alert('success')
  }
  script.onerror = function(){
      alert('fail')
  }
})

server代码

else if (path === "/pay") {
        var amount = fs.readFileSync('./bd', 'utf-8')
        var newAmount = amount - 1
        fs.writeFileSync('./bd', newAmount)
        response.setHeader('Content-Type', 'application/javascript')
        response.statusCode = 200
        response.write('alert("我是pay")')
        response.end()
    } 

但是每次都会在html里面创建一个会执行的script


ASBHRU.png

我们可以把onload()的内容写到server里面

response.write(`
        alert("success")
        window.location.reload()
        `)

这样服务器返回了一个在浏览器里执行的代码,但是刷新体验不好,那么我们可以在服务器上改amount

   response.write(`
        alert("success")
        amount.innerText = amount.innerText - 1
        `)

每次打钱都会新建一个script,我们当然要删掉他们

script.onload = function(e){
    e.currentTarget.remove()
  }
  script.onerror = function(e){
      alert('fail')
      e.currentTarget.remove()
  }

这里有一个重要的概念,就是script的src可以请求不同的域名,我们做两个网站,让他们彼此交流 frank.com:8001和jack.com:8002

click.addEventListener('click',function(e){
  let script = document.createElement('script')
  script.src = 'http://jack.com:8002/pay'
  document.body.appendChild(script)
  script.onload = function(e){
    e.currentTarget.remove()
  }
  script.onerror = function(e){
      alert('fail')
      e.currentTarget.remove()
  }
})
ASs6G6.png

jack.com操作它的数据库打钱


ASy9J0.png

但是这里有一个问题,就是jack.com的程序员需要对frank.com的页面细节了解的很彻底,有很大的耦合性

else if (path === "/pay") {
        var amount = fs.readFileSync('./bd', 'utf-8')
        var newAmount = amount - 1
        fs.writeFileSync('./bd', newAmount)
        response.setHeader('Content-Type', 'application/javascript')
        response.statusCode = 200
        response.write(`
        ${query.callbackName}.call(undefined,'success')
        `)
        response.end()
    }
window.yyy = function(result){
    alert(`我得到的结果是${result}`)
}
click.addEventListener('click',function(e){
  let script = document.createElement('script')
  script.src = 'http://jack.com:8002/pay?callbackName=yyy'
  document.body.appendChild(script)
  script.onload = function(e){
    e.currentTarget.remove()
  }
  script.onerror = function(e){
      alert('fail')
      e.currentTarget.remove()
  }
})
AS6sgK.png

我们把回调函数的名字写在callbackName里面,服务器通过query.callbackName.call()来调用前端写好的函数,如果把'success'换成JSON那么就是JSONP了!

 response.write(`
          ${query.callbackName}.call(undefined,{
            "success":true,
            "left":${newAmount} 
          })
        `)     // JSON + Padding 叫做JSONP

xxx一般用一个随机数代替

 let script = document.createElement('script')
  let functionName = 'frank'+parseInt(Math.random()*1000000,10)
  window[functionName] = function(result){
    if(result === 'success'){
        amount.innerText = amount.innerText - 1
    }else{

    }
  }

当我调用完这个函数后,就删除这个函数

 script.onload = function(e){
    e.currentTarget.remove()
    delete window[functionName]
  }
  script.onerror = function(e){
      alert('fail')
      e.currentTarget.remove()
      delete window[functionName]
  }

其实jQuery已经封装好了


click.addEventListener("click", function(e) {

    // 使用jQuery
    $.ajax({
        url: "http://jack.com:8002/pay",
        jsonp: "callback",
        dataType: "jsonp",
        success: function( response ) {
            if(response === 'success') {
                amount.innerText = amount.innerText - 1
            }
        }
    });

问:JSONP为什么不支持POST?
答:因为JSONP是通过动态创建script来实现的,动态创建script的时候只能用get没有办法用post

相关文章

  • 写一个JSONP

    首先让我们来先写一个server html代码 main.js代码 返回html会同时返回里面内联的main.js...

  • ES6解决跨域

    实现一个jsonp函数,仅需要支持jsonp(url)一种调用方式即可(仅有url一个传参),例如: jsonp(...

  • Promise 封装 jsonp

    这里写一个 Promise 封装 jsonp 请求到 QQ 音乐数据的 demo。 jsfiddle在线演示地址 ...

  • 前端 JSONP 原理及 Node 模拟 JSONP

    1、关于 JSONP 请求的注意项 a、JSONP 请求只支持 GET 形式 b、JSONP 请求返回的是一个自定...

  • VUE JSONP知识点详解

    JSONP jsop介绍 jsonp原理 jsonp发送到并不是一个ajax请求,它其实是创建一个动态script...

  • 紧接上篇,jQuery调用jsonp,并且在页面上展示

    在上篇中提到了spring4.1+支持jsonp的调用,做了个例子,用来在页面上展示jsonp:(js写的丑了点,...

  • demo——简单使用Jsonp跨域请求

    #认识jsonp jsonp,就是调用函数的时候,随便传递一个数据。仅此而已。就像这样: #了解jsonp jso...

  • jsonp原理和实现

    是时候彻底搞清jsonp了! jsonp主要是利用了脚本不受 同源策略 限制的"BUG",算是一种简单的hack写...

  • 用JSONP抓取数据

    jsonp,vue,前端很多网站都是用jsonp传输数据的,可以抓取这些数据。提供一个封装好的jsonp方法。首先...

  • Jsonp解决跨域4

    Jsonp解决跨域4 1、jsonp总结 jsonp是一个非官方协议 它是一个约定 前后端需要约定的参数 默认ca...

网友评论

    本文标题:写一个JSONP

    本文链接:https://www.haomeiwen.com/subject/bwqspqtx.html