1、同源策略
所谓同源是指,域名,协议,端口相同。当一个浏览器的打开一个页面时中,页面请求非同源的URL时,请求能正常响应,但是浏览器会判断请求的URL和本页面的URL是否同源,
如果非同源的话,就会在本页面返回一个错误,并且得不到返回的数据。
2、解决跨域的方法
2.1、CORS通信实现跨域请求
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
只要同时满足以下两大条件,就属于简单请求。
(1) 请求方法是以下三种方法之一:(也就是说如果你的请求方法是什么put、delete等肯定是非简单请求) HEAD GET POST (2)HTTP的头信息不超出以下几种字段:(如果比这些请求头多,那么一定是非简单请求) Accept Accept-Language Content-Language Last-Event-ID Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain,也就是说,如果你发送的application/json格式的数据,那么肯定是非简单请求,vue的axios默认的请求体信息格式是json的,ajax默认是urlencoded的。
对于简单的请求只需要我么请求URL的响应函数views里面配置响应头Access-Control-Allow-Origin
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script> function func(name) { alert(name) } </script> <script src="http://127.0.0.1:8001/base/"></script> </head> <body> <form> <input type="button" id="id_button" value="button"> </form> <script src="/static/jquery-2.1.4.min.js"> </script> <script> $("#id_button").click(function () { $.ajax({ url:'http://127.0.0.1:8001/base/', type:'GET', success:function () { alert('1111') } }) }) </script> </body> </html>
from django.shortcuts import render,HttpResponse def index(request): return render(request,'index.html') def base(request): response = HttpResponse('func("wuzhiib")') response['Access-Control-Allow-Origin'] ='http://127.0.0.1:8000' # 在我们请求的URL的views里面设置,’*‘表示允许所有URL通过 return response
浏览器对简单请求和复杂请求的处理是不一样的
* 简单请求和非简单请求的区别? 简单请求:一次请求 非简单请求:两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用于数据传输。 * 关于“预检” - 请求方式:OPTIONS - “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息 - 如何“预检” => 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过 Access-Control-Request-Method => 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过 Access-Control-Request-Headers
如果在Ajax请求中加入ContentType就会使普通的请求变成复杂的请求。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script> function func(name) { alert(name) } </script> <script src="http://127.0.0.1:8001/base/"></script> </head> <body> <form> <input type="button" id="id_button" value="button"> </form> <script src="/static/jquery-2.1.4.min.js"> </script> <script> $("#id_button").click(function () { $.ajax({ url:'http://127.0.0.1:8001/base/', type:'GET', contentType:'application/json', success:function () { alert('1111') } }) }) </script> </body> </html>
对对于复杂的请求要在对应的头里面做设置,Contenttype只需要在响应头里面设置X-Content-Type-Options
from django.shortcuts import render,HttpResponse from django.http import JsonResponse # Create your views here. def index(request): return render(request,'index.html') def base(request): response = HttpResponse('func("wuzhiib")') response['Access-Control-Allow-Origin'] ='http://127.0.0.1:8000' response['Access-Control-Allow-Headers'] = "content-type" #复杂请求ContentType的跨域方法 return response
3、JsonP实现跨域
非同源的URL不能阻止src属性的请求,也就是说通过src属性我们能跨域请求数据
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script> function func(name) { alert(name) } </script> <!--利用script标签来实现跨域请求,返回的是func(''wuzhibin),相当于实现上面func函数的调用--> <script src="http://127.0.0.1:8001/base/"></script> </head> <body> <form> <input type="button" id="id_button" value="button"> </form> <script src="/static/jquery-2.1.4.min.js"> </script> <script> $("#id_button").click(function () { $.ajax({ url:'http://127.0.0.1:8001/base/', type:'GET', contentType:'application/json', success:function () { alert('1111') } }) }) </script> </body> </html>
from django.shortcuts import render,HttpResponse def index(request): return render(request,'index.html') def base(request): response = HttpResponse('func("wuzhiib")') response['Access-Control-Allow-Origin'] ='http://127.0.0.1:8000' response['Access-Control-Allow-Headers'] = "content-type" #复杂请求ContentType的跨域方法 #浏览器报错因为 MIME 类型(“text/html”)不匹配(X-Content-Type-Options: nosniff) response['X-Content-Type-Options'] ="application/json" return response
注意如果如果不配之响应头的X-Content-Type-Options属性,浏览器会报一个错误
上面就是Jsonp实现的原理,这个只是哟个简单的例子,一般我们都需要能够动态的实现标签的创建
动态创建script标签
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script src="/static/jquery-2.1.4.min.js"> </script> <button onclick="f()">sendAjax</button> <!--动态创建标签Script--> <script> function addScriptTag(src){ var script = document.createElement('script'); script.setAttribute("type","text/javascript"); script.src = src; document.head.appendChild(script); // document.body.removeChild(script); } // 接受返回数据的回调函数 function func(name){ alert("hello"+name) } function f(){ addScriptTag("http://127.0.0.1:8001/base/") } </script> </body> </html>
views.py与上面一直
from django.shortcuts import render,HttpResponse def index(request): return render(request,'index.html') def base(request): response = HttpResponse('func("wuzhiib")') response['Access-Control-Allow-Origin'] ='http://127.0.0.1:8000' response['Access-Control-Allow-Headers'] = "content-type" #复杂请求ContentType的跨域方法 #浏览器报错因为 MIME 类型(“text/html”)不匹配(X-Content-Type-Options: nosniff) response['X-Content-Type-Options'] ="application/json" return response
上面的例子中,我们在前端定义函数的时,另一个域明显是不知道我们我们前端定义的是func的,所以我们需要将函数名传给跨域的views函数动态生成返回函数名
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script src="/static/jquery-2.1.4.min.js"> </script> <button onclick="f()">sendAjax</button> <!--动态创建标签Script--> <script> function addScriptTag(src){ var script = document.createElement('script'); script.setAttribute("type","text/javascript"); script.src = src; document.head.appendChild(script); // document.body.removeChild(script); } // 接受返回数据的回调函数 function func(name){ var name=name alert("hello"+name) } function f(){ addScriptTag("http://127.0.0.1:8001/base/?callbacks=func") } </script> </body> </html>
from django.shortcuts import render,HttpResponse def index(request): return render(request,'index.html') def base(request): # 返回给跨域请求的数据 data = 'wuzhibin' #获取跨域请求的函数名 callback = request.GET.get('callbacks') print(callback) response = HttpResponse("%s('%s')"%(callback,data)) response['Access-Control-Allow-Origin'] ='http://127.0.0.1:8000' response['Access-Control-Allow-Headers'] = "content-type" #复杂请求ContentType的跨域方法 #浏览器报错因为 MIME 类型(“text/html”)不匹配(X-Content-Type-Options: nosniff) response['X-Content-Type-Options'] ="application/json" return response
Ajax中使用Jsonp
在实际的用处中,在Ajax中使用跨站请求数据的场景比较多一些。而不是单独生产动态生成一个Script标签去请求数据
from django.shortcuts import render,HttpResponse from django.http import JsonResponse def index(request): return render(request,'index.html') def base(request): import json # 返回给跨域请求的数据,一般都是字典的格式 data = { 'key1':'value1', 'key2':'value2', 'k3':'value3', 'k4':'value4', } #等到跨域的方法名 callbacks = request.GET.get("callbacks") response =HttpResponse("%s('%s')"%(callbacks,json.dumps(data))) response['Access-Control-Allow-Origin'] ='http://127.0.0.1:8000' response['Access-Control-Allow-Headers'] = "content-type" #复杂请求ContentType的跨域方法 #浏览器报错因为 MIME 类型(“text/html”)不匹配(X-Content-Type-Options: nosniff) response['X-Content-Type-Options'] ="application/json" return response
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script src="/static/jquery-2.1.4.min.js"> </script> <button onclick="f()">sendAjax</button> <!--动态创建标签Script--> <script> function f(){ $.ajax({ //大家会发现我的type请求方法没写,默认是get请求 url:"http://127.0.0.1:8001/base/", dataType:"jsonp", jsonp: 'callbacks', jsonpCallback:"SayHi" }); } function SayHi(arg){ // 将字符串转发为字典的格式 var data = JSON.parse(arg); alert(data.key1); //k1是后端返回的数据中的键值对的键 } </script> </body> </html>
也能通过Ajax的success函数获取跨域的数据,这种方式比较简单些
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script src="/static/jquery-2.1.4.min.js"> </script> <button onclick="f()">sendAjax</button> <!--动态创建标签Script--> <script> function f(){ $.ajax({ //大家会发现我的type请求方法没写,默认是get请求 url:"http://127.0.0.1:8001/base/", dataType:"jsonp", jsonp: 'callbacks', jsonpCallback:"SayHi", success:function (rag) { var data = JSON.parse(rag) alert(data.key1) } }); } </script> </body> </html>
views.py函数和上面保持一致