美文网首页
jsonp相关知识2019-12-31

jsonp相关知识2019-12-31

作者: 饥人谷_小霾 | 来源:发表于2019-12-31 11:42 被阅读0次

    一、数据库是什么鬼


    1. 文件系统是一种数据库;
    2. MySQL 是一种数据库;

    只要能长久地存数据,就是数据库

    1. 111
    2. 222

    ①. 111
    ②. 222
    ③. 333

    二、用文件当数据库


    例:用数据库做加法:

    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 path = request.url 
      var query = ''
      if(path.indexOf('?') >= 0){ query = path.substring(path.indexOf('?')) }
      var pathNoQuery = parsedUrl.pathname
      var queryObject = parsedUrl.query
      var method = request.method
    
      /******** 从这里开始看,上面不要看 ************/
    
      console.log('HTTP 路径为\n' + path)
        if(path == '/style.css'){
          var string = fs.readFileSync('./style.css','utf8')
          response.setHeader('Content-Type', 'text/css; charset=utf-8')
          response.write(string)
          response.end()
        }else if(path == '/main.js'){
          var string = fs.readFileSync('./main.js','utf8')
          response.setHeader('Content-Type', 'text/javascript; charset=utf-8')
          response.write(string)
          response.end()
        }else if(path == '/'){
          var string = fs.readFileSync('./index.html','utf8')   
          response.setHeader('Content-Type', 'text/html; charset=utf-8')
          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:

    <body>
        <h5>你的账户余额是:<span id="amount">100</span></h5>
        <button id="button">付款1块钱</button>
        <script>
            button.addEventListener('click',(e)=>{
                let n = amount.innerText
                let number = n - 0 //将字符串转换为数字
                let newNumber = n - 1
                amount.innerText = newNumber
            })
        </script>
    </body>
    
    

    以上代码改变的余额只是 html 里的数据,而不是后台的数据库里 的数据,也就是刷新网页后数据又还原了;

    第二步:接下来,尝试创建一个极简的数据库,然后通过前台的点击改变数据库的数据:明显改变后台数据库需要用到 post 请求,而不是get 请求,因此首先尝试用 form 表单;(如果是【post】,那么表单数据将放在请求体中被发送出去。如果是【get】,那么表单数据将会追加到查询字符串中,以查询字符串的形式提交到服务端。)
    变更后增加代码如下:

         else if(path === '/pay' && method.toUpperCase() === 'POST'){
          var amount = fs.readFileSync('./db','utf8')
          var newAmount = amount - 1
          if(Math.random() > 0.5){
            fs.writeFileSync('./db', newAmount)
            response.write('success')
          }else{
            response.write('fail')
          }
          response.end()
        }
    
    
        <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="1" height="200px"></iframe>
    
    

    每次提交 form 表单后页面都会刷新,怎么解决???
    如上:引入 iframe 标签,让提交 form 表单 后在iframe 里刷新。response.write('success') 与 response.write('fail') 也会显示在 iframe 里;

    第三步:能不能不用刷新且不用引入 ifame 标签???
    解决方法:不用 form 标签单发请求,用其他标签如:a 标签,img 标签,link标签,script 标签 等.......

    三、用<img src="">发请求


    1. <img src="">尝试:
      console.log('HTTP 路径为\n' + path)
        if(path === '/style.css'){
          var string = fs.readFileSync('./style.css','utf8')
          response.setHeader('Content-Type', 'text/css; charset=utf-8')
          response.write(string)
          response.end()
        }else if(path === '/main.js'){
          var string = fs.readFileSync('./main.js','utf8')
          response.setHeader('Content-Type', 'text/javascript; charset=utf-8')
          response.write(string)
          response.end()
        }else if(path === '/'){
          var string = fs.readFileSync('./index.html','utf8')   
          var amount = fs.readFileSync('./db','utf8') // 100,那么db的类型是啥???
          string = string.replace('&&&amount&&&',amount)
          response.setHeader('Content-Type', 'text/html; charset=utf-8')
          response.write(string)
          response.end()
        }else if(path === '/pay'){
          var amount = fs.readFileSync('./db','utf8')
          var newAmount = amount - 1
          if(Math.random() > 0.5){
            fs.writeFileSync('./db', newAmount)
            response.setHeader('Content-Type','image/png') //由于实际并没有响应一个图片文件,导致statusCode = 200 依然 alert('fail')
            response.statusCode = 200
            response.write('success')
          }else{
            response.statusCode = 400
            response.write('fail')
          }
          response.end()
        }else{
          response.statusCode = 404
          response.setHeader('Content-Type','text/html;charset=utf-8')
          response.write('找不到对应的路径')
          response.end()
        }
    
    
        <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="1" height="200px"></iframe> -->
        <button id="button">付款1块钱</button>
        <script>
            button.addEventListener('click',(e)=>{
                let image = document.createElement('img')
                image.src = '/pay'
                image.onload = function(){
                    alert('success')
                }
                image.onerror = function(){
                    alert('fail')
                }
            })
        </script>
    
    

    <img src="">的方式发请求时,只要 createElement 就会 get ,不需要添加到 html 里。实际测试时发现可以修改后台数据,但是只会执行alert('fail'),原因可能是没有实际 响应一个图片文件。修改成功后,依然需要刷新页面才能显示新数据,alert('fail')alert('success)可以提示用户付款结果。

    接下来,实际在响应中返回一个图片测试,修改如下:

        }else if(path === '/pay'){
          var amount = fs.readFileSync('./db','utf8')
          var newAmount = amount - 1
          if(Math.random() > 0.5){
            fs.writeFileSync('./db', newAmount)
            response.setHeader('Content-Type','image/jpg')
            response.statusCode = 200
            response.write(fs.readFileSync('./000.jpg')) //传一个真实的图片
          }else{
            response.statusCode = 400
            response.write('fail')
          }
          response.end()
    
    

    在如实响应一个图片文件后,alert 结果就正常了;
    目前还有一个问题: 用户需要手动刷新页面才能看到余额的变化,怎么解决????

    下一步:不用手动刷新,如下:
    index.html

                image.onload = function(){
                    alert('success')
                    window.location.reload() // 加这句,自动刷新
                }
    
    

    不用手动刷新的另一种思路,在 image.onload 里面直接修改#amount里面的内容,让页面显示的值与后台一样。

    三、用<script></script>发请求

    如下:
    index.html

    <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>
        <script src="./main.js"></script>
    </body>
    
    

    <script></script>发请求与 img 不同,script 仅仅只是创建的话不会发送请求,需要添加到body里面才行;

    server.js变更:

        }else if(path === '/pay'){
          var amount = fs.readFileSync('./db','utf8')
          var newAmount = amount - 1
          if(Math.random() > 0.5){
            fs.writeFileSync('./db', newAmount)
            response.setHeader('Content-Type','application/javascript')
            response.statusCode = 200
            response.write('') // 响应一个空的js文件
          }else{
            response.statusCode = 400
            response.write('fail')
          }
          response.end()
        }
    
    

    测试中发现,每success一次,都会在body里添加一个src='/pay'的script的标签;
    然后将response.write('') // 响应一个空的js文件变更为response.write('alert("success")') // 响应一个空的js文件,后发现响应的js文件会被执行...............因此可以将点击后的监听放到响应里;
    如下:

        }else if(path === '/pay'){
          var amount = fs.readFileSync('./db','utf8')
          var newAmount = amount - 1
    
          fs.writeFileSync('./db', newAmount)
          response.setHeader('Content-Type','application/javascript')
          response.statusCode = 200
          response.write(`
            alert("付款成功")
            window.location.reload()
          `) //此处使用了ES6的书写规则..
    
          response.end()
    
    

    如果不想刷新整个页面可以改成:

          response.write(`
          alert("付款成功")
          //window.location.reload()
          amount.innerText = amount.innerText - 1
          `)
    
    

    最后还有个问题:没点击一次,就会在body里面增加一个script标签.........................怎么解决?????

    四、删除script标签(请求完成后)


    在script里的内容执行完后删除script标签:
    在前端监听script即可:

                script.onload = function(e){
                    // alert('success')
                    // window.location.reload()
                    e.currentTarget.remove()
                }
                script.onerror = function(e){
                    alert('fail')
                    e.currentTarget.remove()
                }
    
    

    以上都是请求的同一域名下的文件,接下来尝试请求不同域名下的js文件。

    五、请求另一个网站的script


    注意:请首先更新你的 nodejs-test 代码(server.js),否则会报错
    环境配置:在hosts文件里配置:

    127.0.0.1  frank.com
    127.0.0.1  jack.com
    
    

    这样就有两个域名了,虽然ip地址一样;
    然后在 git bash开两个node server:

    Administrator@PC-20160402SHGE MINGW64 /j/web-project/node-demo (master)
    $ PORT=8001 node server.js
    监听 8001 成功
    请用在空中转体720度然后用电饭煲打开 http://localhost:8001
    
    Administrator@PC-20160402SHGE MINGW64 /j/web-project/node-demo (master)
    $ PORT=8002 node server.js
    监听 8001 成功
    请用在空中转体720度然后用电饭煲打开 http://localhost:8002
    
    

    在浏览器可以分别打开http://jack.com:8002/http://frank.com:8001/,只是这两个网址的源代码一样;
    到此,环境就准备好了。

    测试:让frank.com的前端访问jack.com的后端。
    变更代码如下:
    html

    <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 = '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()
                }
            })
        </script>
    </body>
    
    

    server

    else if(path === '/pay'){
        var amount = fs.readFileSync('./db','utf8')
        var newAmount = amount - 1
    
        fs.writeFileSync('./db', newAmount)
        response.setHeader('Content-Type','application/javascript')
        response.statusCode = 200
        response.write(`
        alert("付款成功")
        // window.location.reload()
        amount.innerText = amount.innerText - 1
        `)
        response.end()
    }
    
    

    测试结果,在frank.com的页面点击时能请求到jack.com的后端响应的script文件。
    以上代码还存在一个问题:jack.com的后端需要对frank.com的页面细节了解很清楚,并且需要在后端写一段js代码,这样 耦合 成都太紧,可操作性太差;

    怎么解除这种耦合的紧密性?????

    方法:
    frank.com的前端事先在click里面定义一个方法:

    window.xxx = function(result){
        if(result === 'success'){
            alert("付款成功")
            // window.location.reload()
            amount.innerText = amount.innerText - 1
        }else{
            alert("付款失败")
        }
    }
    
    

    然后,jack.com的后端的响应中调用这一个方法:

    response.write(`         //这个不是单引号....
    xxx.call(undefined,'success')
    `)
    
    

    以上还有点小问题:jack.com的后端得找frank.com的前端询问事先定义的方法名,怎么解决???
    优化:可以通过在请求中加入查询参数将函数名传过去;
    如下:
    html

    script.src = 'http://jack.com:8002/pay?callbackName=xxx'
    
    

    server

    response.write(`         //这个不是单引号....
      ${query.callbackName}.call(undefined,'success')
    `)
    
    

    这样,解耦完成。。。。

    总结:需要解决两个网站之间的交流问题时,由于 script 标签可以请求其他网站的文件,因此可以使用 script 来解决;基本思路就是:如果A站要请求B站的一个数据,那么A站首先通过 script 标签向B站发一个请求,B站收到请求后准备把数据传给A站时,只要调用A站传过来的一个函数,并将数据作为被调用函数的参数,最后B站将这个调用作为响应内容响应给A站,这样交流就完成了。------------------如果被调用函数的参数是JSON,那么,这就是JSONP了。

    六、什么是JSONP


    JSONP出现的目的:为了要解决两个网站之间的交流。
    JSONP的工作过程:
    假定现在有两个网站frank.com和jack.com,并且frank.com 的前端要向jack.com 的后端请求一个数据。
    请求方:frank.com 的前端程序员(浏览器)
    响应方:jack.com 的后端程序员(服务器)
    其工作过程如下:

    1. 请求方创建script,script的src 指向响应方,同时传递一个查询参数?callbackName=yyy;
    2. 响应方根据查询参数callbackName=yyy,构建形如:
      1. yyy.call(undefined,'请求方要的数据')
      2. yyy('请求方要的数据')
        这种格式的响应;
    3. 由于响应的内容是script类型的,请求方(浏览器)收到响应后,会直接执行响应的内容,也就是执行yyy.call(undefined,'请求方要的数据');
    4. 这样请求方就得到了不同网址的后端的数据。

    在书写JSONP时有些默认的约定:

    1. callbackName -> callback
    2. yyy -> 随机数,比如abc48489312668()

    上面整个过程就是 JSONP。

    一个符合约定的JSONP:
    html

    <body>
        <h5>你的账户余额是:<span id="amount">&&&amount&&&</span></h5>
        <button id="button">付款1块钱</button>
        <script>
            button.addEventListener('click',(e)=>{
                let functionName = 'xxx' + parseInt(Math.random() * 1000000,10)
                window[functionName] = function(result){
                    if(result === 'success'){
                        alert("付款成功")
                        amount.innerText = amount.innerText - 1
                    }else{
                        alert("付款失败")
                    }
                }
    
                let script = document.createElement('script')
                script.src = 'http://jack.com:8002/pay?callback=' + functionName
                document.body.appendChild(script)
                script.onload = function(e){
                    e.currentTarget.remove()
                }
                script.onerror = function(e){
                    alert('fail')
                    e.currentTarget.remove()
                }
            })
        </script>
        <script src="./main.js"></script>
    </body>
    
    

    server

    var http = require('http')
    var fs = require('fs')
    var url = require('url')
    var port = process.env.PORT || 8888;
    
    var server = http.createServer(function(request, response){
    
      var temp = url.parse(request.url,true)
      var path = temp.pathname
      var query = temp.query
      var method = request.method
    
      /******** 从这里开始看,上面不要看 ************/
    
      console.log('HTTP 路径为\n' + path)
        if(path === '/style.css'){
          var string = fs.readFileSync('./style.css','utf8')
          response.setHeader('Content-Type', 'text/css; charset=utf-8')
          response.write(string)
          response.end()
        }else if(path === '/main.js'){
          var string = fs.readFileSync('./main.js','utf8')
          response.setHeader('Content-Type', 'text/javascript; charset=utf-8')
          response.write(string)
          response.end()
        }else if(path === '/'){
          var string = fs.readFileSync('./index.html','utf8')   
          var amount = fs.readFileSync('./db','utf8') // 100,那么db的类型是啥???
          string = string.replace('&&&amount&&&',amount)
          response.setHeader('Content-Type', 'text/html; charset=utf-8')
          response.write(string)
          response.end()
        }else if(path === '/pay'){
          var amount = fs.readFileSync('./db','utf8')
          var newAmount = amount - 1
          fs.writeFileSync('./db', newAmount)
          response.setHeader('Content-Type','application/javascript')
          response.statusCode = 200
          response.write(`
            ${query.callback}.call(undefined,'success')
          `)
          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)
    
    

    用jQuery重新写,如下:
    html

    <body>
        <h5>你的账户余额是:<span id="amount">&&&amount&&&</span></h5>
        <button id="button">付款1块钱</button>
        <script>
            button.addEventListener('click',(e)=>{
                $.ajax({
                    url:"http://jack.com:8002/pay",
                    dataType:"jsonp",
                    success:function(response){
                        if(response === 'success'){
                            amount.innerText = amount.innerText - 1
                        }
                    }
                })
            })
    
        </script>
    
    

    【JSONP的总结】
    总结: JSONP方式就是通过 script 标签加载数据的方式去获取数据,然后将这个数据接口当做 JS 代码来执行(实际是绕过了浏览器的同源策略,把数据直接通过script标签去执行)。提前在页面上声明一个函数,函数名通过接口传参的方式传给后台,后台解析到函数名后在原始数据上「包裹」这个函数名,发送给前端,即JSONP 需要对应接口的后端的配合才能实现。

    缺点:1.JSONP只支持GET请求; 2.XMLHttpRequest相对于JSONP有着更好的错误处理机制

    六、Cross-Origin Resource Sharing


    CORS 是W3C 推荐的一种新的官方方案,能使服务器支持 XMLHttpRequest 的跨域请求。CORS 实现起来非常方便,只需要增加一些 HTTP 头,让服务器能声明允许的访问来源。

    值得注意的是,通常使用CORS时,异步请求会被分为简单请求和非简单请求,非简单请求的区别是会先发一次预检请求。

    【简单请求】

    使用下列方法之一且没有人为设置对 CORS 安全的首部字段集合之外的其他首部字段:

    GET

    HEAD

    POST

    Access-Control-Allow-Origin: * 表明该资源可以被任意外域访问。

    【非简单请求】

    使用了下面任一 HTTP 方法:

    PUT

    DELETE

    CONNECT

    OPTIONS

    TRACE

    PATCH

    发送真正请求前会先发送预检请求,同时携带了下面两个首部字段:

    Access-Control-Request-Method: POST

    Access-Control-Request-Headers: X-PINGOTHER

    八.postMessage


    window.postMessage(message,targetOrigin) 方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。

    相关文章

      网友评论

          本文标题:jsonp相关知识2019-12-31

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