jsonp相关知识2019-12-31

2019-12-31  本文已影响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方法。

上一篇下一篇

猜你喜欢

热点阅读