资讯专栏INFORMATION COLUMN

JSONP原理

testHs / 604人阅读

摘要:高级部分前后端联动浏览器服务器数据库是什么文件系统是一种数据库是一种数据库也是一个软件只要能长久地存数据,就是数据库前后端如何配合接下来我们用一个文件充当数据库实际上数据库的存储内容本质就是一个带有结构的文件捋一捋前后端交互的过程代码在这里

jsonp

javascript高级部分:前后端联动,浏览器+服务器

1数据库是什么

文件系统是一种数据库
MySQL 是一种数据库,也是一个软件
只要能长久地存数据,就是数据库

2前后端如何配合?

接下来我们用一个文件充当数据库(实际上数据库的存储内容本质就是一个带有结构的文件),捋一捋前后端交互的过程.

代码在这里
几次代码的变更在历史commit里

首先写一个首页

首页

我的余额为&&&amount&&&

再写服务器的代码

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

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

    if (path === "/") {  // 如果用户请求的是 / 路径
        var string = fs.readFileSync("./index.html")  // 就读取 index.html 的内容
        var amount = fs.readFileSync("./db","utf-8")//****从数据库(db文件)同步读取数据,放到amount变量里
        string = string.toString().replace("&&&amount&&&",amount)//将后台的amount替换为前台的占位符&&&amount&&&

        response.setHeader("Content-Type", "text/html;charset=utf-8")  // 设置响应头 Content-Type
        response.write(string)   // 设置响应消息体
        response.end();
    } else if (path === "/style.css") {   // 如果用户请求的是 /style.css 路径
        var string = fs.readFileSync("./style.css")
        response.setHeader("Content-Type", "text/css")
        response.write(string)
        response.end()
    } else if (path === "/main.js") {  // 如果用户请求的是 /main.js 路径
        var string = fs.readFileSync("./main.js")
        response.setHeader("Content-Type", "application/javascript")
        response.write(string)
        response.end()
    } else if(path === "/pay" && method.toUpperCase() === "POST"){//如果请求的路径是pay且方法为post
        var amount = fs.readFileSync("./db","utf-8")
        var newAmount = amount-1;
        fs.writeFileSync("./db",newAmount);
        response.write("success")
        response.end()
    }else {  // 如果上面都不是用户请求的路径
        response.statusCode = 404
        response.setHeader("Content-Type", "text/html;charset=utf-8")  // 设置响应头 Content-Type
        response.write("找不到对应的路径,你需要自行修改 index.js")
        response.end()
    }

    // 代码结束,下面不要看
    console.log(method + " " + request.url)
})

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

最后在创建一个名为db的文本文件充当数据库

接着我们开启服务器
node index.js 8888


接着在浏览器中输入地址http://localhost:8888
当用户打开浏览器输入ip地址,就会向后台发送请求,服务器发现地址是/之后执行if elsepath==="/"分支,执行下面的代码:

if (path === "/") {  // 如果用户请求的是 / 路径
        var string = fs.readFileSync("./index.html")  // 就读取 index.html 的内容
        var amount = fs.readFileSync("./db","utf-8")//****从数据库(db文件)同步读取数据,放到amount变量里
        string = string.toString().replace("&&&amount&&&",amount)//将后台的amount替换为前台的占位符&&&amount&&&

        response.setHeader("Content-Type", "text/html;charset=utf-8")  // 设置响应头 Content-Type
        response.write(string)   // 设置响应消息体
        response.end();
    } 

接着点击提交按钮,就会向后台发起post请求,请求路径为/pay,于是进入path === "/pay"分支,执行下面的代码,使数据库(这里数据库用一个文本文件代替)中的金额减一

else if(path === "/pay" && method.toUpperCase() === "POST"){//如果请求的路径是pay且方法为post
        var amount = fs.readFileSync("./db","utf-8")
        var newAmount = amount-1;
        fs.writeFileSync("./db",newAmount);
        response.write("success")
        response.end()
    }

然后会发现/pay页面返回了success

返回刚才的首页,刷新后,会发现金额减少了1.

也可以模拟后台修改数据库成功或失败

 else if(path === "/pay" && method.toUpperCase() === "POST"){//如果请求的路径是pay且方法为post
        var amount = fs.readFileSync("./db","utf-8")
        var newAmount = amount-1;
        if(Math.random()>0.5){//模拟成功或失败
            fs.writeFileSync("./db",newAmount);
            response.write("success")
        }else{
            response.write("fail")
        }
        response.end()
    }
3 优化体验:用image和script发送请求

但是刚才那样体验非常不好,因为要返回,要刷新.在2005年以前,网页的前后端交互都是这样.但现在是8012年了,我们需要优化一下用户体验

不光form表单可以发送请求,a标签(需要点击),link标签,script,图片,都可以发送请求

3.1用image发送请求

那我们先试试用img发送一个请求
当浏览器发现html里面有一个img时,会自动发送请求,请求路径就是img的src.但是有一个缺点.发送的请求只能是get,没办法改成post.
使用img.onload和img.onerror方法判断请求是成功还是失败,具体操作看下方代码

复习一下返回的状态码:
2开头:成功
3开头:重定向
4开头:客户端错误
5开头:服务器错误

修改首页

首页

我的余额为&&&amount&&&

修改/pay路径的响应

else if(path === "/pay"){//如果请求的路径是pay,接受图片的请求
        var amount = fs.readFileSync("./db","utf-8")
        var newAmount = amount-1;
        if(Math.random()>0.5){//模拟成功或失败
            fs.writeFileSync("./db",newAmount);//成功了就把数据写入数据库
            response.setHeader("Content-Type","image/jpg")//设置返回文件类型为jpg
            response.statusCode = 200;//返回码为200,说明成功
            response.write(fs.readFileSync("./dog.jpg"))//必须返回一个真的图片,不然浏览器还是会认为是失败
        }else{
            response.statusCode = 400;//否则返回码为400,说明失败
            response.write("fail")
        }
        response.end()
    }

然后开启服务器,浏览器输入地址
点击付钱,成功,并刷新

付钱失败时,弹框后浏览器就不刷新了:

也可局部刷新

img.onload = function(){//如果图片请求成功(返回码以2开头)
            alert("付钱成功")
                        amount.innerText = amount.innerText-1;//局部刷新/因为已经知道数据库修改成功,所以无需再进行刷新,直接修改数值即可

        }

还有一个需要注意的点
在下面这段代码中

 response.setHeader("Content-Type","image/jpg")//设置返回文件类型为jpg,
            response.statusCode = 200;//返回码为200,说明成功
            response.write(fs.readFileSync("./dog.jpg"))//必须返回一个真的图片,不然浏览器还是会认为是失败

看看回复的响应
response.write(fs.readFileSync("./dog.jpg"))这句代码中fs.readFileSync("./dog.jpg")参数里只有路径,没有加别的,那么读出来的数据类型为2进制数据,然后"Content-Type","image/jpg"这句代码表示以jpg图片的形式读取这段二进制代码.

复习一下响应的四个部分:

1 协议/版本号 状态码 状态解释
2 Key1: value1
2 Key2: value2
2 Content-Length: 17931
2 Content-Type: text/html
3
4 响应的内容

response.write(fs.readFileSync("./dog.jpg"))这句代码就是响应的第四部分,即响应的内容

3.2用script发送请求

那么我们也可以用script动态的发送请求.原理和img类似,但是必须得把script标签加入到body里面,才会发送请求,而img标签只要创建,就会发送请求.
接下来看代码
首页的发送请求代码

服务器端的代码:

else if(path === "/pay"){//如果请求的路径是pay,接受script的请求
        var amount = fs.readFileSync("./db","utf-8")
        var newAmount = amount-1;

        if(Math.random()>0.5){//模拟成功或失败
            fs.writeFileSync("./db",newAmount);//成功了就把数据写入数据库
            
            response.setHeader("Content-Type","applacation/javascript")//设置返回文件类型为javascript
            response.statusCode = 200;//返回码为200,说明成功
            response.write("alert("我是创建的script请求里面响应的内容")")//响应内容为创建的script标签里面的内容
        }else{
            response.statusCode = 400;//否则返回码为400,说明失败
            response.write("fail")
        }
        response.end()
    }

当我点击打钱的时候,他会动态的创建一个script,这个script会根据src发起请求

进入失败分支:

成功:
先执行请求使用的script里面的语句

每次请求就会创建一个新的script标签,如果响应成功了,就执行响应里面的javascript语句,这个script里面的语句就是返回的响应的第四部分的内容,执行完之后才会触发onload事件

3.2.1优化用户体验

那么既然请求返回的响应可以执行,我们就不需要写onload了,直接把onload里面的代码写在响应的第四部分不就可以了?没错

接来下修改一下代码,删掉onload代码,将onload里面的代码写在服务器端.将前台请求成功要做的事情交给后台来做.

btn.onclick = function(){
        let script = document.createElement("script");
        script.src = "/pay";

        document.body.appendChild(script);//这句话一定要加上,这是与使用图片请求不一样的地方

        //删除onload代码,交给后台来做
        script.onerror = function(){//返回码是4开头
            alert("付钱失败");
        }
    }

后台只需要将金额数量减1即可,不需要刷新,增加用户体验

 else if(path === "/pay"){//如果请求的路径是pay,接受script的请求
        var amount = fs.readFileSync("./db","utf-8")
        var newAmount = amount-1;

        if(Math.random()>0.5){//模拟成功或失败
            fs.writeFileSync("./db",newAmount);//成功了就把数据写入数据库

            response.setHeader("Content-Type","applacation/javascript")//设置返回文件类型为javascript
            response.statusCode = 200;//返回码为200,说明成功
            response.write("amount.innerText = amount.innerText-1")//响应内容为创建的script标签里面的内容,返回后会立即执行*********
        }else{
            response.statusCode = 400;//否则返回码为400,说明失败
            response.write("fail")
        }
        response.end()
    }else

最后在做一件事情

因为每次不管成功失败,都会创建一个script标签,那么可否在请求结束后,将标签删除?
可以.
修改前台onload和onerror代码即可,不管请求成功失败,都移除script标签

script.onload = function(e){
            e.currentTarget.remove();//添加移除代码
        }
        script.onerror = function(e){
            e.currentTarget.remove();
        }
3.2.2总结 SRJ(server rrendered javascript--服务器返回的javascript)

以上方法就叫做SRJ,服务器返回的javascript,即不是在前台写的javascript
这是在ajax出现之前,后端程序员想出来的前后台无刷新,局部更新页面内容的交互方法

4 请求另个一网站的script

script请求的内容不受域名限制.当前网站的可以请求其他网站的js代码,例如jquery

可以引入jquery

所以刚才的SRJ很危险,假如只用了get方法,那么像付钱这种操作很容易被伪装成功,导致别人的恶意攻击
例如我进入一个钓鱼网站
那个网站直接请求
script.src = "http://alipay.com/pay?usname=mataotao&amount=100000"
这样只要进入网站执行这个请求,岂不是可以随意给自己的账户打钱?
所以只用get不安全.
所以很多危险操作都得用POST请求,例如打钱必须验证用户,做各种支付相关的安全防范,手机验证码之类的.

下面我们模仿一下不同的网站请求对方的后台的过程
首先修改一下本地的hosts文件,将两个域名都指向127.0.0.1,假装成两个不同的网站

hosts文件相当于一个个人的DNS,当你访问某个域名时,是先通过hosts进行解析的,没有找到才进一步通过外网DNS服务器进行解析

如果是windows系统:
windows系统hosts文件位置及操作
如果是linux或者Unix
地址为/etc/hosts


添加代码,那么如果我在本地访问mataotao.com和jack.com,他首先会进127.0.0.1,而不是去外网解析dns
这样我们就假装成为了两个网站,实际上两个网站的内容是一样的,知识用来做实验

然后开两个服务器,使用两个不同的端口来监听(模拟两个不同的网站都开启了服务器,都在监听)
监听8001

监听8002

这样我们在地址栏输入mataotao.com:8001就进入了第一个网站,在地址栏输入jack.com:8002
就进入了第二个网站.

这样两个个网站都可以接受请求,都可以访问,只不过内容一样而已
那么接下来我们来模仿不同网站之间的请求互动

修改前端SRJ请求代码的请求路径,改为script.src = "http://jack.com:8002/pay";
那么我们打开mataotao.com:8001就进入了第一个网站,点击按钮后,意思就是请求http://jack.com:8002/pay这个第二个网站的地址.我们来试一试


结果如图所示

这是成功的时候

可以看到,两个完全不同的网站,他们两个是可以互相调script的

这时候的意思是:我是用第一个网站(mataotao.com)打开了第二个网站(jack.com)的付钱功能!
(由于我为了演示,这两个网站用了同样的代码,同一个数据库,我们假设第一个网站he第二个网站代码不同)

5 jsonp

这时候出现一个问题:
jack.com的后端程序员需要对mataotao.com的页面细节了解的很清楚.
这样的缺点是后台对前台需要了解得太深入了.比如说你是node或者php腾讯后端程序员,可是我竟然还要对阿里巴巴网站的一个按钮的一个细节非常了解.耦合度太高,前后端彼此之间关系太紧密了.如果后端程序员对页面细节不了解,代码就写不下去了.这样对前后端分离不友好

5.1解决前后端耦合度太高的问题

解耦(解决耦合问题)
解决方法:

后台程序员:我只把我后台应该改的数据库,然后我写一个callback函数,将成功和失败的状态传递给前台.这个函数就是我后端程序员做完这些事情成功之后要前端程序员去做的事情.然后执行这个函数,这个函数的内容交给前端去写.

前端程序员:我不管后端如何修改数据库,我事先写好成功和失败的函数,成功了就返回给我一个成功的提示,然后我根据提示执行成功的函数,如果给我的提示是失败,就执行失败的函数.
接下来写代码理解:
修改服务器端的代码:

response.write(`${query.callbackName}.call(undefined,"success")`)//先获取查询字符串里的callbackName即从前台传过来的回调函数的函数名,然后再执行他,并把函数的参数定为success

添加前台index.html的代码

添加的这几句代码的解释:
首先
script.src = "http://jack.com:8002/pay?callbackName=xxx";
在请求时带上查询参数,指定callbackName为xxx,以便给后台程序员调用
接着

response.write(`${query.callbackName}.call(undefined,"success")`)

后台程序员获得前太程序员的查询参数,并执行名为查询参数的函数xxx

执行xxx,并且将相应的内容作为回调函数的第一个参数传回去,这个参数为字符串"success"

window.xxx = function(result){
        alert("我是前端程序员写的函数,经过后端程序员调用才执行")
        alert(`后端返回回来的函数参数是:${result}`)
    }

执行结果:


也可以根据后台传过来的参数,修改前台的逻辑

window.xxx = function(result){
        if(result === "success"){
            amount.innerText = amount.innerText - 1;
        }
    }

那么如果我们返回的参数不是字符串,而是对象呢?
这也是可以的,例如我们修改一下代码

else if(path === "/pay"){//如果请求的路径是pay,接受script的请求
        var amount = fs.readFileSync("./db","utf-8")
        var newAmount = amount-1;

        if(Math.random()>0.5){//模拟成功或失败
            fs.writeFileSync("./db",newAmount);//成功了就把数据写入数据库

            response.setHeader("Content-Type","applacation/javascript")//设置返回文件类型为javascript
            response.statusCode = 200;//返回码为200,说明成功
            response.write(`${query.callbackName}.call(undefined,{
                "success":true,
                "left":${newAmount}
            })`)//把函数的参数定为一个对象
        }else{
         
            response.statusCode = 400;//否则返回码为400,说明失败
            response.write("fail")
        }
        response.end()
    }

前端:

    window.xxx = function(result){
        if(result.success === true){
            amount.innerText = amount.innerText - 1;
        }
    }
5.2jsonp的含义和理解

我请求一个script,script调用一个函数的同时,把第一个参数设置为要返回的数据,不管这个数据是对象还是字符串.然后人们把这种调用script并返回字符串或者对象的使用方法叫做是jsonp,这就是jsonp!.只是名字比较奇怪,其实真名的名字应该是:动态标签跨域请求!即利用动态标签进行跨域请求的技术

这种方法的好处是可以解决跨域的问题.例如刚才的例子,两个网站如果域名不一样,那么第一个网址的前台和另一个网址后台的服务器无法进行数据交换(由于浏览器的同源策略).但是使用script标签就可以避免这个问题.利用script请求,可以请求道第二个网站的json或字符串数据,这种方法就叫做jsonp

JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。由于同源策略,一般来说位于 server1.example.com 的网页无法与不是 server1.example.com的服务器沟通,而 HTML 的
 response.write(`${query.callback}.call(undefined,{
                "success":true,
                "left":${newAmount}
            })`)


1随机生成的查询字符串
2返回的根据随机生成的函数名的数据

6 使用jQuery中的jsonp

jQuery使用jsonp非常简单

只要这样修改前台的代码.后台不用改 url不需要写callback查询参数,因为jQuery会自动给你生成

btn.onclick = function () {

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

需要注意的一点是,jsonp不是ajax中的一种.不要背jquery误导

7 面试题:jsonp为什么不能用post请求

答:

jsonp是通过动态创建script实现的

动态创建script的时候只能用get,没有办法用post

6 什么json

JSON 语法

6.1JSON 语法规则

在 JS 语言中,一切都是对象。因此,任何支持的类型都可以通过 JSON 来表示,例如字符串、数字、对象、数组等。但是对象和数组是比较特殊且常用的两种类型:

对象表示为键值对

数据由逗号分隔

花括号保存对象

方括号保存数组

6.2JSON 键/值对

JSON 键值对是用来保存 JS 对象的一种方式,和 JS 对象的写法也大同小异,键/值对组合中的键名写在前面并用双引号 "" 包裹,使用冒号 : 分隔,然后紧接着值:

{"firstName": "Json"}

这很容易理解,等价于这条 JavaScript 语句:

{firstName : "Json"}
6.3 JSON 与 JS 对象的关系

很多人搞不清楚 JSON 和 Js 对象的关系,甚至连谁是谁都不清楚。其实,可以这么理解:
JSON 是 JS 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串

var obj = {a: "Hello", b: "World"};
//这是一个对象,注意键名也是可以使用引号包裹的
var json = "{"a": "Hello", "b": "World"}"; //这是一个 JSON 字符串,本质是一个字符串
6.4JSON 和 JS 对象互转

要实现从对象转换为 JSON 字符串,使用 JSON.stringify() 方法:

var json = JSON.stringify({a: "Hello", b: "World"}); //结果是 "{"a": "Hello", "b": "World"}"

要实现从 JSON 转换为对象,使用 JSON.parse() 方法:

var obj = JSON.parse("{"a": "Hello", "b": "World"}"); //结果是 {a: "Hello", b: "World"}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/96367.html

相关文章

  • JSONP原理及实现跨域方式

    摘要:同源策略限制了我们无法通过原生的对象获取到数据。的原理其实不复杂浏览器的同源策略把跨域请求都禁止了的标签是例外,可以突破同源策略从其他来源获取数据由上可得,我们可以通过标签引入文件,然后通过一系列操作获取数据。上面三点便是实现跨域的原理。 今天做页面时,后台给了个接口:https://a.a.com/a/a.json,我页面的上线地址是:http://b.b.com。显而易见,因为浏览...

    The question 评论0 收藏0
  • JSONP原理及JQUERY JSONP的使用

    摘要:同源策略在中有一个很重要的安全性限制,被称为同源策略。然而,当进行一些比较深入的前端编程的时候,不可避免地需要进行跨域操作,这时候同源策略就显得过于苛刻。 JSONP原理 JSON和JSONP   JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。对于JSON大家应该是很了解了吧,不是很清楚的朋友可以去json.org上了解下,简单易懂。   ...

    suosuopuo 评论0 收藏0
  • JSONP原理剖析

    摘要:运行一下页面,成功弹出提示窗口,的执行全过程顺利完成到这里为止的话,相信你已经能够理解的客户端实现原理了吧剩下的就是如何把代码封装一下,以便于与用户界面交互,从而实现多次和重复调用。 先说说JSONP是怎么产生的: 其实网上关于JSONP的讲解有很多,但却千篇一律,而且云里雾里,对于很多刚接触的人来讲理解起来有些困难,小可不才,试着用自己的方式来阐释一下这个问题,看看是否有帮助。 1、...

    DangoSky 评论0 收藏0
  • jsonp原理与实现

    摘要:概述是一种跨域通信的手段,它的原理其实很简单首先是利用标签的属性来实现跨域。可靠的实现添加回调函数拼接传递的是一个匿名的回调函数,要执行的话,暴露为一个全局方法出错处理使用示例来源个人博客 1. 概述 jsonp是一种跨域通信的手段,它的原理其实很简单: 首先是利用script标签的src属性来实现跨域。 通过将前端方法作为参数传递到服务器端,然后由服务器端注入参数之后再返回,实现服...

    SillyMonkey 评论0 收藏0
  • jsonp原理介绍及Promise封装

    摘要:什么叫是填充式或参数式的简写,是通过请求跨域接口,获取数据的新实现方式的实现原理动态创建标签,因为标签是没有同源策略限制,可以跨域的。具体看接下来的实现这个是库的具体实现,建议下载来研究一下,最好自己动手写一遍。 什么叫jsonp? jsonp是json with padding(填充式json或参数式json)的简写,是通过ajax请求跨域接口,获取数据的新实现方式 jsonp的实现...

    ninefive 评论0 收藏0
  • 关于javascript跨域及JSONP原理与应用

    摘要:因为同源策略的限制,我们不能在与外部服务器进行通信的时候使用。这个是跨域服务器取数据的接口,参数为回调函数的名字,返回的格式为原理首先在客户端注册一个然后把的名字传给服务器。 一、同源策略 同源策略,它是由Netscape提出的一个著名的安全策略,现在所有的可支持javascript的浏览器都会使用这个策略。 为什么需要同源策略,这里举个例子: 假设现在没有同源策略,会发生什么事...

    CoderBear 评论0 收藏0

发表评论

0条评论

testHs

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<