基于jQuery的Ajax实现:

jQquery中创建XMLHttpRequest对象就没有兼容性问题了,而且不需要前面的四个步骤,直接使用$.ajax(),通过设置相关的参数,如提交的方法,url,数据等,一次性完成所有步骤及功能。$.ajax()是jQuery实现ajax的最底层方法。

$.ajax(
url:
type:"POST"  or “GET”
......
)

高级一些的封装:

$.get()    :直接就体现了GET提交
$.post()   :直接接体现了POST提交

上面是在$.ajax()基础上的更高级封装,上述的方法至少不需要设置提交方法参数了。
<1>$.get(url, [data], [callback], [datatype])
<2>$.post(url, [data], [callback], [datatype])         //datatype: text|html|json|script,数据的类型,要求后台返回的数据的类型。

不提交数据时两种方法的请求头:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<button name="btn1" onclick="func1();">ajax提交</button>
<script src="/static/js/jquery-3.6.0.js"></script>
<script>function func1() {test()}function test() {$.get("/app01/jqajaxtest/")// $.post("/app01/jqajaxtest/")}
</script>
</body>
</html>
def jqueryajax(req):return render(req,"jqueryajax.html")def jqajaxtest(req):print("req.GET:",req.GET)return HttpResponse("ok111")

由上面两个结果,只是请求方法不同。

传递数据:

get修改参数:
1) $.get("/app01/jqajaxtest/?a=bbq&b=10")  $.get()的第一种传递数据方式,直接拼接网址后

后台打印:

2)  $.get("/app01/jqajaxtest/",{name:'bbq',a:100})   $.get()的第二种传递数据方式,参数第二个位置。

结果与第一种相同,ajax将参数的字典键值对给拼接到了请求网址的后面。

post修改参数:

1) $.post("/app01/jqajaxtest/",{aa:'小老虎',bb:23})     POST方法传递数据,数据在第二个参数位置

2) $.post("/app01/jqajaxtest/",{aa:'小花猫',bb:44},function (data) {
                            alert(data); }),增加第三个参数,回调函数

回调函数就是POST方法执行完毕后,再调用执行这个函数,函数的参数data接收POST方法返回的数据,就是后台jqajaxtest(req)返回的数据,即ok111。

还有第四个参数,是数据类型,是告诉后台返回给前端的数据类型,如果不匹配,则回调函数不执行。

关于回调函数,其参数都有哪些呢?可以使用arguments参数来查看:
$.post("/app01/jqajaxtest/",{aa:'小花猫',bb:44},function (data) { console.log(arguments); })

显示参数公有3个,分别是ok111,success和{...},其中上面写的data对应第一个参数 ,第二个参数是状态信息,两个值‘success’和‘error’,第三个参数是一个对象,这个对象就是XMLHttpRequest对象。

$.getJson()  相当于$.get(   ,datatype=Json)
$.getscript()  用于即时加载文件,作用类似<script src="" />,区别是<script src="" />是在网页加载时就加载完成了,而$.getscript() 是在需要的时候执行这一句再加载。

在static目录下建立一个test.js文件:

点击按钮后,执行fun1——>test时,才加载test.js文件,加载了test.js,才能使用其中的add函数。

$.ajax()的使用:

$.ajax({
"name":"abc,
})          ——一个花括号,里面写键值对

$.ajax("url",{
"name":"abc,
})    ——先写一个url,然后在加上花括号

$.ajax({
url:"",
type:"POST",
data:{      //此时data是一个json形式的对象
    a:1,
    b:2
    },
processData:false,   //声明当前的data数据是否进行转码或预处理,默认true,即预处理。如果为false,对data:{a:1,b:2}会调用json对象的toString方法,即{a:1,b:2}.toString()最后得到一个[object,Object]形式的结果。当data是一个dom结构或xml数据时,数据不要进行处理
contentType:      //浏览器告诉服务端数据类型格式,默认是x-www-form-urlencoded
traditional:true   //对于{a:1,b:[3,4]},如果不设traditional,后台拿到的是<QueryDict: {'a': ['1'], 'b[]': ['3', '4']}>,
dataType:json|xml|。。。,//服务器需要给浏览器的数据类型
success:function(){},//请求成功后的回调函数
beforeSend:function(){},//在发送请求的数据之前执行的操作
complete:function(){},    //不管请求成功还是失败,最后都执行这个过程
error:function(){} ,      // 请求失败时执行
statusCode:{
        ‘403’:function(jqXHR,textStatus,err){},
        ‘400’:function(){}
},   //根据反馈的状态码执行不同的处理          在后台视图文件中,可以使用HttpResponse.status_code = '400'来模拟返回相应状态码

})

测试traditional:false:

对于data:{a:1,b:[3,4]},在浏览器端看到的提价数据:

后台打印的req.POST是 <QueryDict: {'a': ['1'], 'b[]': ['3', '4']}>

改为traditional:true,浏览器端提交的是:

后台打印的req.POST是 <QueryDict: {'a': ['1'], 'b': ['3', '4']}>

关于processData:

如果设为true,且traditional设置为true,data在后台打印:<QueryDict: {'a': ['1'], 'b': ['3', '4']}>

如果设置为false,打印成<QueryDict: {'[object Object]': ['']}>

 关于JSON

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。JSON是用字符串来表示Javascript对象;—— json字符串就是js对象的一种表现形式(字符串的形式)

python中学过json模块,通过测试,来看看json字符串和json对象到底是什么

首先,json对象是JavaScript中的概念,python中没有json对象的概念

JavaScript中JSON对象的对象语法:

实例

var obj = { "name":"runoob", "alexa":10000, "site":null }

  • JSON 对象使用在大括号({})中书写。
  • 对象可以包含多个 key/value(键/值)对。
  • key 必须是字符串,value 可以是合法的 JSON 数据类型(字符串, 数字, 对象, 数组, 布尔值或 null)。
  • key 和 value 中使用冒号(:)分割。
  • 每个 key/value 对使用逗号(,)分割。

访问对象值可以使用点号(.)来访问对象的值,如obj.name;可以使用中括号([])来访问对象的值,如obj["name"]。

这是json对象的写法,花括号里面跟键值对,并且key必须是字符串,即必须使用双引号引起来,其值value则必须是以下类型:

  • 数字    (整数或浮点数)
  • 字符串 (在双引号中)
  • 逻辑值 (true 或 false)
  • 数组    (在方括号中)
  • 对象    (在花括号中,引号用双引)
  • null

在前端,即JavaScript中,可以通过 JSON.parse() 方法将数据转换为 JavaScript 对象注意是转为JavaScript对象,而不是JSON对象。而方法的参数是一个字符串,也可以是上面的JavaScript的数据类型的值。

        var json1 = {"pp":0}; //JSON对象var json1str = '{"aa":10}';//JSON对象字符串var json2 = 10;var json2str = '10';var json3 = true;var json3str = 'false';var json4 = ["aa","bb"];var json4str = '["aa","bb"]';var json5 = {"aa":{"bb":true}};var json5str = '{"aa":{"bb":true}}';var json6 = new Array();json6[0] = 20;json6[1] = "hello";console.log("1:",typeof json1)console.log("2:",typeof json1str)// console.log(typeof JSON.parse(json1)) //json1本身已经是JSON对象,不能在进行parseconsole.log(JSON.stringify(json1))  //stringify方法将JSON对象转变为JSON字符串console.log(typeof JSON.stringify(json1)) //string类型console.log(typeof JSON.parse(JSON.stringify(json1)))  //object类型console.log("3:",typeof JSON.parse(json1str))console.log("4:",typeof json2)console.log("5:",typeof json2str)console.log("6:",typeof JSON.parse(json2))console.log("7:",typeof JSON.parse(json2str))console.log("8:",typeof json3)console.log("9:",typeof json3str)console.log("10:",typeof JSON.parse(json3))console.log("11:",typeof JSON.parse(json3str))console.log("12:",typeof json4)console.log("13:",typeof json4str)//console.log(typeof JSON.parse(json4)) //  类型为object的json4,不能再被parseconsole.log("14:",typeof JSON.parse(json4str))console.log("15:",typeof json5)console.log("16:",typeof json5str)console.log("17:",typeof json5.aa)  //也是object类型console.log("18:",typeof JSON.parse(json5str))console.log("19:",typeof json6)

上述程序的运行结果:

由此可以得出的结论是:当类型是object时,不能使用parse再进行转换,如数组、JSON对象,而其他如数字,布尔型,会被解析成数字,布尔型对象,而符合数字,布尔型,数组,JSON对象的语法的字符串,会被解析成数字,布尔型,数组,JSON对象,普通的字符串无法再解析,会报错。

以上是在前端,即在JavaScript中的解析过程:parse将json字符串解析成对象,stringify是将对象解析成json字符串。

在后台,即Python中对JSON的处理,使用json模块,有dumps()和loads()方法,dumps方法是将python对象输出为符合JSON对象规则的字符串,loads方法是将符合JSON对象规则的字符串解析成python对象。在后台,即在python中,没有JSON对象的概念,只是JSON对象字符串。

 import jsonprint("req.GET:",req.GET)print('req.POST:',req.POST)json1 = {"pp": 0}json1str = '{"aa":10}'json2 = 10json2str = '10'json3 = Truejson3str = "True"json4 = ["aa", "bb"]json4str = '["aa","bb"]'json5 = {'aa': {'bb': True}}json5str = '{"aa":{"bb":true}}'print("1:type:",type(json1))print("2:type:",type(json.dumps(json1)),json.dumps(json1))print("3:type:",type(json.loads(json1str)),json.loads(json1str))print("4:type:",type(json2),json.dumps(json2))# print("5: type:",json.loads(json2))  #int型不能再loadsprint("6: type:",type(json.loads(json2str)),json.loads(json2str))print("7:type:",type(json3))# print("8:type:",json.loads(json3)) #the JSON object must be str, bytes or bytearrayprint("9:type:",type(json.dumps(json3)),json.dumps(json3))# print("10:type:",type(json.loads(json3str)),json.loads(json3str)) #符合布尔型的字符串不能被loads成布尔型对象print("11:type:",type(json4))# print("12:type:",type(json.loads(json4)))  #list不能再loadsprint("13:type:", type(json.dumps(json4)),json.dumps(json4))print("14:type:", type(json.loads(json4str)),json.loads(json4str))# print("15:type",type(json.loads(json5))) #dict不能再loadsprint("15:type", type(json.dumps(json5)),json.dumps(json5))print("16:type:",type(json.loads(json5str)),json.loads(json5str))

运行的结果:

结论是:python的json模块,其loads方法只能是对字符串进行解析,最终解析成python相对应的对象,dumps方法是将python的对象解析成符合JSON对象规则的字符串

要符合JSON规则的字符串,特别要注意,JSON中的布尔型要用true,而python中的对象是True,JSON中的字符串一定要用双引号。

因为python中json的dumps只是输出JSON的字符串,是字符串,而视图函数最后不管是通过render()函数还是直接通过HttpResponse()函数,返回给前端的都是字符串,所以,后台的python中render()函数、HttpResponse()函数中的参数不能是直接的如字典、列表等对象,必须经过json.dumps()解析成字符串传递给前端。

同样,前端接收的都是字符串,要进行操作,先要进行JSON.parse(),将字符串解析成JSON对象或JavaScript对象,在进行操作。JSON字符串是一个中间状态,在JavaScript对象和python对象间过渡。

python与json对象的对应:中间的"-->"就是JSON字符串

 python         -->        jsondict                      objectlist,tuple                arraystr,unicode               stringint,long,float            numberTrue                      trueFalse                     falseNone                      null

现在再看$ajax()方法中data:项传递的数据,其不是JSON对象,只是花括号包起来的键值对,且其key没有引号,最后传递到后台的数据,其值都是字符串,所以如果是数字的话,需要进行转换。

dataType是指明后台的传递给前端的数据是什么类型,如dataType:"json"指定后台传递的数据为json格式,这句话的意思是要求后台传递的数据是符合json字符串规格的字符串。

$.ajax({url:"/app01/jqajaxtest/",type:"POST",data:{a:1,b:[3,4],c:"hello"},traditional:true,processData:true,dataType:"json",success:function (data) {console.log(data);}})

后台反馈:

dic = {'key1':12345}
return HttpResponse(json.dumps(dic))

在前端的结果显示是一个json对象。

如果反馈是:return HttpResponse('hello')   注意是单引号

前端增加一个alert:

success:function (data) {alert('执行');console.log(data);console.log(typeof data)
}

上面的反馈在前端没有执行,即success的回调函数没有执行,说明请求没有成功。

修改反馈为:return HttpResponse(“hello”)   注意是双引号

依然没有成功,success函数没有执行。

修改反馈为:return HttpResponse('“hello”')   注意是单引号中加双引号

此时alert被执行,log结果 ,是一个字符串了。

对于数字来说,后台反馈:

return HttpResponse(12345)
return HttpResponse(‘12345’)
return HttpResponse(“12345”)
结果都为

return HttpResponse('“12345”'),则返回的是12345和string,这时作为字符串

return HttpResponse(“'12345'”),则success不执行,因为反馈的是'12345',不符合json字符串规则。

其他各类型可以自行测试。

说明后台反馈前端必须是符合json规范的字符串。前端的逻辑是接收后台的数据,使用JSON.parse()进行解析,解析成功,就调用success中的回调函数,否则就不执行success这个选项了。

关于跨域请求

同源策略机制
      浏览器有一个很重要的概念——同源策略(Same-Origin Policy)。所谓同源是指:域名,协议,端口相同。不同源的客户端脚本(javascript、ActionScript)在没明确授权的情况下,不能读写对方的资源。

简单的来说,浏览器允许包含在页面A的脚本访问第二个页面B的数据资源,这一切是建立在A和B页面是同源的基础上。

如果Web世界没有同源策略,当你登录淘宝账号并打开另一个站点时,这个站点上的JavaScript可以跨域读取你的淘宝账号数据,这样整个Web世界就无隐私可言了。

同源策略是浏览器的策略。

有src属性的标签,可以跨域请求:

<script src=""></script>,这里的src中的地址可以是跨域的

 jsonp的js实现

JSONP是JSON with Padding的略称。可以让网页从别的域名(网站)那获取资料,即跨域读取数据。

它是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)。

JSONP就像是JSON+Padding一样(Padding这里理解为填充)

测试跨域访问:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<button name="btn1" onclick="func1();">ajax提交</button>
<script src="/static/js/jquery-3.6.0.js"></script>
<script src="http://127.0.0.1:8001/app01/jqueryajax/"></script>
{# 上面的语句src使用了完整的路径名,并且不同源,即协议+IP+端口与请求不同 #}
<script>function func1() {test()}function test() {$.ajax({url:"/app01/jqajaxtest/",type:"POST",data:{a:1,b:[3,4],c:"hello"},traditional:true,processData:true,dataType:"json",success:function (data) {alert('success执行');console.log(data);console.log(typeof data);},beforeSend:function () {alert('before');},error:function () {alert('error');console.log(arguments);}})}
</script>
</body>
</html>
def jqajaxtest(req):import jsonprint("req.GET:",req.GET)print('req.POST:',req.POST)return HttpResponse("hello")

关键就是<script src="http://127.0.0.1:8001/app01/jqueryajax/"></script>这一句。

我们同样的项目在不同的端口上启动,一个在8000,一个在8001。

然后访问http://127.0.0.1:8000/app01/jqueryajax/,此时在执行到<script src="http://127.0.0.1:8001/app01/jqueryajax/"></script>时,就进行了跨域的访问了。

在浏览器中访问结果如下:

出现了错误信息,CORB屏蔽MIME类型为text/html的响应。

修改一下:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<button name="btn1" onclick="func1();">ajax提交</button>
<script src="/static/js/jquery-3.6.0.js"></script>
<script>function ff1(w) {alert(w);}     //定义一个函数
</script>
<script src="http://127.0.0.1:8001/app01/jqajaxtest/"></script>
{# 上面的语句src使用了完整的路径名,并且不同源,即协议+IP+端口与请求不同 #}
<script>function func1() {test()}function test() {$.ajax({url:"/app01/jqajaxtest/",type:"POST",data:{a:1,b:[3,4],c:"hello"},traditional:true,processData:true,dataType:"json",success:function (data) {alert('success执行');console.log(data);console.log(typeof data);},beforeSend:function () {alert('before');},error:function () {alert('error');console.log(arguments);}})}
</script>
</body>
</html>
def jqajaxtest(req):import jsonprint("11111111111111")print("req.GET:",req.GET)print('req.POST:',req.POST)dic = {'key1':12345}# HttpResponse.status_code = '400'print(HttpResponse().get(header="X-Content-Type-Options"))# print("===:",HttpResponse.has_header("X-Content-Type-Options"))# return HttpResponse("hello")return HttpResponse("ff1('kkk');",headers={"Content-Type":"text/javascript",})

运行结果:

对于语句:<script src="http://127.0.0.1:8001/app01/jqajaxtest/"></script>

跨域请求成功,返回了   ff1('kkk');,并作为JavaScript语句执行,因为网页中定义了ff1(w)函数,就把跨域的数据"kkk"打印出来了。

关键是设置HTML头:headers={"Content-Type":"text/javascript",},否则会被CORB阻塞。

以上就是JSONP的简单实现模式,或者说是JSONP的原型:创建一个回调函数,然后在远程服务上调用这个函数并且将JSON 数据形式作为参数传递,完成回调

将JSON数据填充进回调函数,这应该就是JSONP的JSON+Padding的含义吧。

一般情况下,我们希望这个script标签能够动态的调用,而不是像上面因为固定在html里面所以没等页面显示就执行了,很不灵活。我们可以通过javascript动态的创建script标签,这样我们就可以灵活调用远程服务了。

<button onclick="f()">submit</button><script>function addScriptTag(src){var script = document.createElement('script');script.setAttribute("type","text/javascript");script.src = src;document.body.appendChild(script);document.body.removeChild(script);}function fun1(arg){alert("hello"+arg)}function f(){addScriptTag("http://127.0.0.1:8002/get_byjsonp/")}
</script>

上面的代码实际上就是在点击按钮时生成<script>标签:

<script src="http://127.0.0.1:8002/get_byjsonp/" type="text/javascript"></script>

以上的代码默认后台知道前端的函数,如果后台不知道呢?这就需要前端将函数名传给后台:

<button onclick="f()">submit</button><script>function addScriptTag(src){var script = document.createElement('script');script.setAttribute("type","text/javascript");script.src = src;document.body.appendChild(script);document.body.removeChild(script);}function SayHi(arg){alert("Hello "+arg)}function f(){addScriptTag("http://127.0.0.1:8002/get_byjsonp/?callbacks=SayHi")//注意这里的callbacks=SayHi,就是传递前端的函数名给后台}
</script>----------------------views.py
def get_byjsonp(req):func=req.GET.get("callbacks")return HttpResponse("%s('yuan')"%func)  #后台动态的调用前端的函数,根据前端传来的函数名调用。

jQuery对JSONP的实现

jQuery框架也当然支持JSONP,可以使用$.getJSON(url,[data],[callback])方法:

<script type="text/javascript">$.getJSON("http://127.0.0.1:8002/get_byjsonp?callback=?",function(arg){alert("hello"+arg)});
</script>

这里的callback=?,在后台,会收到键值对callback=“dfsafds'”,是一个随机的字符串,作为函数名,前端知道其与后边的function(arg)是关联的,相当于一个匿名函数的名。

要注意的是在url的后面必须添加一个callback参数,这样getJSON方法才会知道是用JSONP方式去访问服务,callback后面的那个问号是内部自动生成的一个回调函数名。

此外,如果说我们想指定自己的回调函数名,或者说服务上规定了固定回调函数名该怎么办呢?我们可以使用$.ajax方法来实现:

<script type="text/javascript" src="/static/jquery-2.2.3.js"></script><script type="text/javascript">$.ajax({url:"http://127.0.0.1:8002/get_byjsonp",dataType:"jsonp",jsonp: 'callbacks',jsonpCallback:"SayHi"});function SayHi(arg){alert(arg);}
</script>#--------------------------------- http://127.0.0.1:8002/get_byjsonpdef get_byjsonp(req):callback=req.GET.get('callbacks')print(callback)return HttpResponse('%s("yuan")'%callback)

上面的jsonp: 'callbacks',和jsonpCallback:"SayHi",最终会拼接成:callbacks=“SayHi”,即指明函数名。

最简单的形式还是通过回调函数来处理:

<script type="text/javascript" src="/static/jquery-2.2.3.js"></script><script type="text/javascript">$.ajax({url:"http://127.0.0.1:8002/get_byjsonp",dataType:"jsonp",            //必须有,告诉server,这次访问要的是一个jsonp的结果。jsonp: 'callbacks',          //jQuery帮助随机生成的:callbacks="wner"success:function(data){alert(data)}});</script>#-------------------------------------http://127.0.0.1:8002/get_byjsonp
def get_byjsonp(req):callbacks=req.GET.get('callbacks')print(callbacks)                 #wner  return HttpResponse("%s('yuan')"%callbacks)

jsonp: 'callbacks'就是定义一个存放回调函数的键,jsonpCallback是前端定义好的回调函数方法名'SayHi',server端接受callback键对应值后就可以在其中填充数据打包返回了;

jsonpCallback参数可以不定义,jquery会自动定义一个随机名发过去,那前端就得用回调函数来处理对应数据了。

     利用jQuery可以很方便的实现JSONP来进行跨域访问。  

==================================================

关于CORB——Cross-Origin Read Blocking,跨域读阻塞,是一个新的 web 平台安全功能, 它能够帮助减少线程之间的旁路攻击(side-channel attacks);

CORB 的目的是防止浏览器向网页接收某些跨源网络响应,因为这些响应可能包含敏感信息,而且现有的网页功能不需要这些响应。

例如: 它将清除一个从<script> 或者 <img> 标签发起的跨源text/html响应. 将响应的内容,用空值代替。这是网站隔离(Site Isolation)非常重要的一部分.<script> 或者 <img>标签主要通过src=“”属性可以访问不同源的网站信息。

允许加载跨域资源, 意味着存在着安全隐患:
场景: 用户登录了 victim.com 之后, 又去访问了 evil.com 的恶意站点.
在 evil.com 中有 <script src="victim.com/login">的元素, 则浏览器会向 victim.com 发起请求, 且会把敏感信息返回给浏览器. 此时, 用户在 evil.com 页面所在的内存中就留有的 victim.com 的敏感数据;

CORB 会在敏感信息加载到页面内存之前,将其拦截掉,如此,敏感信息既不会暴露于浏览器,也不会进驻内存空间,得到了很好的保护。

CORB 可以减少下面的攻击
Cross-Site Script Inclusion (XSSI)
XSSI 是一种攻击方式. 它通过<script>指向一个不是 JavaScript 代码文件的目标资源, 并且让浏览器执行.具体的情况, 可以看看 JSON_Hijacking_Gareth_Heyes

<script charset="ISO-8859-1" src="polyglot/uploads/xss.jpg"></script>
CORB 阻止了这一类攻击, 因为 CORB 将阻止<script> 标签发起的这种请求;

通过 CPU 预测执行,而引起的旁路攻击 (例如 Spectre).
例如: 攻击者会使用 img标签来加载跨域的文件, 让 JavaScript 在执行过程中, 将进程中的信息暴露给攻击者

<img src="https://example.com/secret.json" />
CORB 通过阻止 JSON 的资源加载到进程的内存中, 可以阻止这一类攻击.

CORB 是如何阻止响应的呢?
当 CORB 判断出需要对响应做出 CORB-protected, 响应则会

响应体被替换为空值
响应头被移除
为了有效地对抗预测执行的旁路攻击,CORB 阻塞必须在响应到达承载跨源请求发起者的进程之前发生。

CORB 不会影响的内容
CORB 并不会影响下面的情况

  • navigation requests或者请求的request destination 是"document", “embed”, “frame”, “iframe”, 或者 “object”.
  • Download requests   :<a href="/images/myw3schoolsimage.jpg" download></a>
  • XHR and fetch()
  • ping, navigator.sendBeacon()
  • <link rel="prefetch" ...>
  • Requests with the following request destination
    • “image” requests like <img> tag, /favicon.ico, SVG‘s <image>, CSS’ background-image, etc.
    • “audio”, “video” or “track”
    • “font”
    • “style”

什么样的内容会被 CORB-protected
当跨域请求回来的数据 MIME type 同跨域标签应有的 MIME 类型不匹配时,浏览器会启动 CORB 保护数据不被泄漏.
例如: script 标签请求的响应是 json. img 标签请求回来的是 json.

目前, 针对下面的响应类型会触发 CORB

  • JSON
  • HTML
  • XML
  • pdf 以及More CORB-protected MIME types - adding protected types one-by-one

浏览器判断响应类型: 浏览器拥有Content_sniffing的功能, 会尝试判断响应类型, 并不依赖Content-Type;除非响应头指定 “X-Content-Type-Options: nosniff”, 此时浏览器会直接根据Content-Type的格式进行处理;

例如:

一个 img 标签, 请求一个 html 的数据

  • 响应体 Body: 是一个 HTML document
  • Content-Type: text/html
  • No X-Content-Type-Options header

此时, 浏览器尝试判断响应内容, 如果发现是 html 标签. 则触发 CORB-protected

JSONP 与 CORB
JSONP 算是一种讨巧的跨域请求方式。JSONP 和 CORB 有什么关系呢?

在某些情况下, JSONP 会触发 CORB

例如:

响应体 Body: 是 js 代码
Content-Type: text/html
X-Content-Type-Options: ‘nosniff’
通过 script 标签, 获取到 html 的内容(因为指定了 nosniff, 浏览器会直接认为响应内容是 text/html). 从而会触发 CORB, 响应会被清空;

如果服务器发送响应头 "X-Content-Type-Options: nosniff",则 script 和 styleSheet 元素会拒绝包含错误的 MIME 类型的响应。这是一种安全功能,有助于防止基于 MIME 类型混淆的***。

简单理解为:通过设置"X-Content-Type-Options: nosniff"响应标头,对 script 和 styleSheet 在执行是通过MIME 类型来过滤掉不安全的文件

服务器发送含有 "X-Content-Type-Options: nosniff" 标头的响应时,此更改会影响浏览器的行为。

如果通过 styleSheet 参考检索到的响应中接收到 "nosniff" 指令,则 Windows Internet Explorer 不会加载“stylesheet”文件,除非 MIME 类型匹配 "text/css"。

如果通过 script 参考检索到的响应中接收到 "nosniff" 指令,则 Internet Explorer 不会加载“script”文件,除非 MIME 类型匹配以下值之一:

  • "application/ecmascript"

  • "application/javascript"

  • "application/x-javascript"

  • "text/ecmascript"

  • "text/javascript"

  • "text/jscript"

  • "text/x-javascript"

  • "text/vbs"

  • "text/vbscript"

JSON、JSONP与CORB

JSON是一种数据交换格式,而JSONP是一种非官方跨域数据交互协议。

JSON是一种基于文本的数据交换方式,或者叫做数据描述格式。

JSON的格式或者叫规则:

JSON能够以非常简单的方式来描述数据结构。

1、JSON只有两种数据类型描述符,大括号{}和方括号[],其余英文冒号:是映射符,英文逗号,是分隔符,英文双引号""是定义符。

2、大括号{}用来描述一组“不同类型的无序键值对集合”(每个键值对可以理解为OOP的属性描述),方括号[]用来描述一组“相同类型的有序数据集合”(可对应OOP的数组)。

3、上述两种集合中若有多个子项,则通过英文逗号,进行分隔。

4、键值对以英文冒号:进行分隔,并且建议键名都加上英文双引号"",以便于不同语言的解析。

5、JSON内部常用数据类型无非就是字符串、数字、布尔、日期、null 这么几个,字符串必须用双引号引起来,其余的都不用,日期类型比较特殊,这里就不展开讲述了,只是建议如果客户端没有按日期排序功能需求的话,那么把日期时间直接作为字符串传递就好,可以省去很多麻烦。

什么是JSONP?

JSONP的产生:

1、Ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面、动态网页、web服务、WCF,只要是跨域请求,一律不准;

2、不过,Web页面上调用js文件时则不受是否跨域的影响(不仅如此,凡是拥有"src"这个属性的标签都拥有跨域的能力,比如<script>、<img>、<iframe>);

3、于是可以判断,当前阶段如果想通过纯web端(ActiveX控件、服务端代理、属于未来的HTML5之Websocket等方式不算)跨域访问数据就只有一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理;

4、恰巧JSON的纯字符数据格式可以简洁的描述复杂数据,更妙的是JSON还被js原生支持,所以在客户端几乎可以随心所欲的处理这种格式的数据;

5、这样子解决方案就呼之欲出了,web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀),显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。

6、客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来非常像AJAX,但其实并不一样。

7、为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

JSONP的客户端具体实现:

1、我们知道,哪怕跨域js文件中的代码(当然指符合web脚本安全策略的),web页面也是可以无条件执行的。

远程服务器remoteserver.com根目录下有个remote.js文件代码如下:

alert('我是远程文件');

本地服务器localserver.com下有个jsonp.html页面代码如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title></title><script type="text/javascript" src="http://remoteserver.com/remote.js"></script>
</head>
<body></body>
</html>

运行结果,页面将会弹出一个提示窗体,显示跨域调用成功。

2、现在我们在jsonp.html页面定义一个函数,然后在远程remote.js中传入数据进行调用。

jsonp.html页面代码如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title></title><script type="text/javascript">var localHandler = function(data){alert('我是本地函数,可以被跨域的remote.js文件调用,远程js带来的数据是:' + data.result);};</script><script type="text/javascript" src="http://remoteserver.com/remote.js"></script>
</head>
<body></body>
</html>

remote.js文件代码如下:

localHandler({"result":"我是远程js带来的数据"});

运行之后查看结果,页面成功弹出提示窗口,显示本地函数被跨域的远程js调用成功,并且还接收到了远程js带来的数据,即{"result":"我是远程js带来的数据"},这里显示的是“我是远程js带来的数据”,因为是data.result。

跨域远程获取数据的目的基本实现了,但是又一个问题出现了,怎么让远程js知道它应该调用的本地函数叫什么名字呢?毕竟是jsonp的服务者都要面对很多服务对象,而这些服务对象各自的本地函数都不相同啊?

3、只要服务端提供的js脚本是动态生成的就行了呗,这样调用者可以传一个参数过去告诉服务端“我想要一段调用XXX函数的js代码,请你返回给我”,于是服务器就可以按照客户端的需求来生成js脚本并响应了。

看jsonp.html页面的代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title></title><script type="text/javascript">// 得到航班信息查询结果后的回调函数var flightHandler = function(data){alert('你查询的航班结果是:票价 ' + data.price + ' 元,' + '余票 ' + data.tickets + ' 张。');};// 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)var url = "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler";// 创建script标签,设置其属性var script = document.createElement('script');script.setAttribute('src', url);// 把script标签加入head,此时调用开始document.getElementsByTagName('head')[0].appendChild(script); </script>
</head>
<body></body>
</html>

这次的代码变化比较大,不再直接把远程js文件写死,而是编码实现动态查询,而这也正是jsonp客户端实现的核心部分。调用的url中传递了一个code参数,告诉服务器我要查的是CA1998次航班的信息,而callback参数则告诉服务器,我的本地回调函数叫做flightHandler,所以请把查询结果传入这个函数中进行调用。服务器很聪明,这个叫做flightResult.aspx的页面生成了一段这样的代码提供给jsonp.html(服务端的实现这里就不演示了,与你选用的语言无关,说到底就是拼接字符串):

flightHandler({           //flightHandler是callback传递的值"code": "CA1998",     //这是code传递的值"price": 1780,        //这是跨域服务器查询的结果"tickets": 5          //这也是跨域服务器查询的结果
});

传递给flightHandler函数的是一个json,它描述了航班的基本信息。运行一下页面,成功弹出提示窗口,jsonp的执行全过程顺利完成!

4、到这里为止,jsonp的客户端实现原理就完成了,剩下的就是如何把代码封装一下,以便于与用户界面交互,从而实现多次和重复调用。

我们这里学习的是jQuery,那么jQuery如何实现jsonp调用?

jQuery使用jsonp的代码

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" ><head><title>Untitled Page</title><script type="text/javascript" src=jquery.min.js"></script><script type="text/javascript">jQuery(document).ready(function(){ $.ajax({type: "get",async: false,url: "http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998",dataType: "jsonp",jsonp: "callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)jsonpCallback:"flightHandler",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据success: function(json){alert('您查询到航班信息:票价: ' + json.price + ' 元,余票: ' + json.tickets + ' 张。');},error: function(){alert('fail');}});});</script></head><body></body></html>

这次没有写flightHandler这个函数也能成功,这就是jQuery的功劳了,jquery在处理jsonp类型的ajax时自动帮你生成回调函数并把数据取出来供success属性方法来调用。

1、ajax和jsonp这两种技术在调用方式上“看起来”很像,目的也一样,都是请求一个url,然后把服务器返回的数据进行处理,因此jquery和ext等框架都把jsonp作为ajax的一种形式进行了封装;

2、但ajax和jsonp其实本质上是不同的东西。ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本。

3、所以说,其实ajax与jsonp的区别不在于是否跨域,ajax通过服务端代理一样可以实现跨域,jsonp本身也不排斥同域的数据的获取。

4、还有就是,jsonp是一种方式或者说非强制性协议,如同ajax一样,它也不一定非要用json格式来传递数据,如果你愿意,字符串都行,只不过这样不利于用jsonp提供公开服务。

总而言之,jsonp不是ajax的一个特例,哪怕jquery等巨头把jsonp封装进了ajax,也不能改变着一点!

以上来源随它去吧——【原创】说说JSON和JSONP,也许你会豁然开朗,含jQuery用例 - 随它去吧 - 博客园

=======================================

Python入门自学进阶-Web框架——8、认识Ajax,与Django交互,基于jQuery相关推荐

  1. Python入门自学进阶-Web框架——34、富文本编辑器KindEditor、爬虫初步

    KindEditor是一个轻量级的富文本编辑器,应用于浏览器客户端. 一.首先是下载:http://kindeditor.net/down.php,如下图 下载后是 解压缩后: 红框选中的都可以删除到 ...

  2. Python入门自学进阶-Web框架——16、Django登录/注册

    以抽屉为原型,实现用户的注册和登录. 基本的界面: 第一个知识点:自动发送验证码到邮箱,也就是实现自动发送邮件的功能: 要自动给别人发送邮件,首先要有自己的邮箱,msg["From" ...

  3. Python入门自学进阶-Web框架——33、瀑布流布局与组合查询

    一.瀑布流,是指页面布局中,在显示很多图片时,图片及文字大小不相同,导致页面排版不美观 如上图,右边的布局,因为第一行第一张图片过长,第二行的第一张被挤到第二列了.示例: def flow(reque ...

  4. Python入门自学进阶——9-网络编程-远程执行命令

    远程执行命令 类似远程终端,输入一个命令,在对端执行.也是网络通信编程的典型应用. socket连接都是一样的,发送内容也是一样的,不同之处是,对端拿到的内容,当做命令执行,然后,将执行的结果反馈给对 ...

  5. Python入门自学进阶——11-协程

    协程,又叫做微线程,纤程.Coroutine.协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈,调度切换时,将寄存器上下文和栈保存,在切换回来时,恢复寄存器山下文和栈.协程能保留上一次调 ...

  6. python入门与进阶

    title: python入门与进阶 categories: python tags: [python] python入门导学 python的特点 是面向对象的编程语言 简介,灵活,优雅,哲学 易于上 ...

  7. Python入门及进阶

    Python入门及进阶 一.python是什么? 二.python基础知识 三.python面向对象 四.文件处理 一.python是什么? python是一种跨平台.解释性.面向对象的高级编程语言. ...

  8. 自学python要看哪些书籍-Python入门自学到精通需要看哪些书籍?

    Python语言在近几年可以算得上如日中天,越来越火爆的同时,学习Python的人也越来越多了.对于不同基础的学习者来讲,学习的重点和方式也许会有差别,但是基础语法永远都是重中之重.在牢牢掌握基础知识 ...

  9. 『Python学习笔记』Python中的异步Web框架之fastAPI介绍RestAPI

    Python中的异步Web框架之fastAPI介绍&RestAPI 文章目录 一. fastAPI简要介绍 1.1. 安装 1.2. 创建 1.3. get方法 1.4. post方法 1.5 ...

最新文章

  1. POJ-2513 Colored Sticks 字典树,欧拉回路
  2. Java8读文件仅需一行代码
  3. 【飞秋】ASP.NET 之 常用类、方法的超级总结,并包含动态的EXCEL导入导出功能,奉上类库源码
  4. while的用法和格式描述符
  5. pytorch中tensor、numpy.array、list三者互相转换
  6. 13幅逻辑图,领略杜克大学的经典思维
  7. OGRE学习笔记(一)通过例子了解场景管理器---------地形创建
  8. Ps 初学者教程,如何用文字增强您的照片?
  9. 如何获取中间层的结果_如何从0开始做大数据治理(上)
  10. 软件下载地址900款正版
  11. CodeSmith连接MySql数据库
  12. 需要点智商才能看懂的恐怖故事,你能看懂多少个?
  13. mysql 5.6 触发器_【mysql】mysql触发器使用示例
  14. 比入赘还简单的——Python Flask Web 框架入门
  15. L.G.Hassebrook团队结构光论文记录
  16. 企业微信三方开发(三):网页授权登录
  17. 快手安全 X 墨菲安全 | 软件供应链安全解决方案完整分享
  18. mysql 5.6 rpm 下载_mysql5.6 rpm包下载
  19. 什么是 jQuery?
  20. 线程wait和notify方法

热门文章

  1. Nod32升级服务器(转)
  2. [QT学习]-调色板|选择文件
  3. 启动Tomcat服务器报错:Several ports (8005, 8080, 8009) required by Tomcat v9.0 Server at localhost are alrea
  4. 软件企业管理——无为而无不为
  5. flutter_engine 交叉编译【自定义编译器(最新)】
  6. 学习记录:Monkey测试
  7. 字节跳动后台开发实习面试回顾
  8. 腾讯云服务器修改ssh端口,简谈腾讯云更换SSH登录端口
  9. 电脑某一个文件夹嵌套太多,导致无法删除的解决方法
  10. HTML5:移动互联网的第二个苹