Django-解决跨域请求(基于js,jQuery的josnp,设置响应头的cors)
同源策略
同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。当一个浏览器的两个tab页中分别打开来 百度和谷歌的页面当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。
url http ip port paht ? 数据
if 协议 IP PORT相同,同源
一,问题描述
1 django 创建2个项目 2 项目一,开启一个send_ajax请求 http://127.0.0.1:8000 3 访问正常 4 5 项目二,没有send_ajax请求 http://127.0.0.1:8010 6 那么这时候如果你通过ajax访问的 url:http://127.0.0.1:8000/send_ajax 7 浏览器会报错,内容是已拦截跨域请求,同源策略禁止读取位于http://127.0.0.1:8000/send_ajax远程资源,原因:cors头缺少 ‘Access-Control-Allow-Origin’
1.1 思考
jsonp是json用来跨域的一个东西。原理是通过script标签的跨域特性来绕过同源策略。
1 跨域请求: 2 ajax请求一定会被拦截 3 4 核心任务:跨域请求的是数据,数据,数据 5 6 通过引入jquery cdn 可以跨域 我们想 是不是script标签不会被拦截呢? 7 <script src=" https://cdn.bootcss.com/jquery/3.2.1/jquery.js "></script>
1.2跨域请求的演变
1 项目一 views 2 import json 3 def send_ajax(request): 4 return HttpResponse("yuan") # 第一次返回字符串 5 return HttpResponse("yuan()") # 第二次返回函数字符串 6 return HttpResponse("yuan('yuan')") # 第三次返回函数参数字符串 7 s='hello world' 8 return HttpResponse("yuan('%s')"%s) # 第四次返回函数字符串数据 9 10 s={"name":"egon","age":122} 11 return HttpResponse("%s('%s')"%(func_name,json.dumps(s))) # 第五次返回函数json字符串 12 13 项目二 html 14 第一次字符串,你要是知道数据是什么还请求干什么?所以这里只是个引子,告诉你可以这样做 15 <script>yuan</script> 16 <script src="http://127.0.0.1:8000/send_ajax"></script> 17 18 19 第二次函数字符串 20 <script>function yuan(){alert(123)}</script> 21 <script src="http://127.0.0.1:8000/send_ajax"></script> 22 23 24 第三次返回函数参数字符串 25 <script>function yuan(arg){alert(arg)}</script> 26 <script src="http://127.0.0.1:8000/send_ajax"></script> 27 28 29 第四次返回函数字符串数据(数据也是字符串) 30 <script>function yuan(arg){alert(arg)}</script> 31 <script src="http://127.0.0.1:8000/send_ajax"></script> 32 33 34 第五次返回函数json字符串 35 <script>function yuan(arg){alert(Json.parse(arg))}</script> 36 <script src="http://127.0.0.1:8000/send_ajax"></script>
1.3问题
1 我不能硬生生的将<script src="http://127.0.0.1:8000/send_ajax"></script>写在html里面,我需要动态创建下面的函数
我们通过触发点击事件函数创建
1 <script> 2 function yuan(arg) { 3 console.log(arg); 4 console.log(JSON.parse(arg)); 5 } 6 </script> 7 8 <script> 9 $(".send_ajax").click(function () { 10 kuayu_request("http://127.0.0.1:8000/send_ajax/") 11 }); 12 function kuayu_request(url) { 13 var $script=$("<script>"); // 创建script标签,是一个jquery对象: <script> 14 $script.attr("src",url); 15 $("body").append($script); 16 $("body script:last").remove(); 17 } 18 </script>
1.4 问题
1 1 问题函数名字是不是可以通过请求传送到后端呢?应该是同步的,可否通过回调函数?
可以通过发送请求的时候带着数据,数据包含你的函数名称,这样传到后端,再让后端返回你这个函数和数据,就可以执行了
1 项目二 html 2 <script> 3 $(".send_ajax").click(function () { 4 kuayu_request("http://127.0.0.1:8000/send_ajax/?callback=bar") 5 }); 6 function kuayu_request(url) { 7 var $script=$("<script>"); // 创建script标签,是一个jquery对象: <script> 8 $script.attr("src",url); 9 $("body").append($script); 10 $("body script:last").remove(); 11 } 12 </script> 13 14 15 项目一 views 16 def send_ajax(request): 17 func_name = request.GET.get('callback') 18 s = 'hello' 19 return HttpResponse("%s('%s')"%(func_name,json.dump(s)))
之前都是引子,下面才是真正的用法。。
ajax的跨域请求写法
1 <script> 2 3 $(".send_ajax").click(function () { 4 5 $.ajax({ 6 url:"http://127.0.0.1:8000/send_ajax/", 7 success:function (data) { 8 alert(data) 9 }, 10 // 跨域请求 <script> 告诉服务器我要什么类型的数据contenttype是告诉服务器我是什么类型数据 11 dataType:"jsonp", 12 jsonp: 'callbacks', //?callbacks=SayHi 相当于这个,问题就是在于前后端的函数名要相同 13 jsonpCallback:"SayHi" 14 }) 15 16 17 }); 18 19 function SayHi(arg) { 20 console.log("hello",arg) 21 } 22 </script> 23 24 {#// ==========================================基于jquery的JSONP的实现3 (推荐)#} 25 26 <script> 27 28 $(".send_ajax").click(function () { 29 30 $.ajax({ 31 url:"http://127.0.0.1:8000/send_ajax/", 32 dataType:"jsonp", // 跨域请求 <script> 33 // callbacks=? ?就是随机字符串了,后端传回来运行的就是success函数,也就是funciton 函数参数是数据 34 jsonp: 'callbacks', 35 success:function (data) { 36 console.log(data) 37 } 38 }) 39 40 }); 41 42 </script>
jsonp的所有实现和应用例子
项目一的views.py
1 import json 2 def send_ajax(request): 3 s={"name":"egon","age":122} 4 return HttpResponse("list('%s')"%json.dumps(s))
项目二的index.html
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.js "></script> 7 </head> 8 <body> 9 <h1>项目2的首页</h1> 10 <button class="send_ajax">send_ajax</button> 11 12 13 {#// ==========================================基于JS的JSONP的实现#} 14 <script> 15 function bar(arg) { 16 console.log(arg); 17 console.log(JSON.parse(arg)); 18 } 19 </script> 20 21 <script> 22 23 $(".send_ajax").click(function () { 24 kuayu_request("http://127.0.0.1:8000/send_ajax/?callback=bar") // 回调函数 25 }); 26 // 创建script添加scr属性,获取数据 27 function kuayu_request(url) { 28 var $script=$("<script>"); // 创建script标签,是一个jquery对象: <script> 29 $script.attr("src",url); 30 $("body").append($script); 31 $("body script:last").remove(); 32 } 33 34 </script> 35 36 37 <hr> 38 39 {#// ==========================================基于jquery的JSONP的实现1#} 40 41 42 <script> 43 44 $(".send_ajax").click(function () { 45 // getJSON帮助我们创建了script标签和src属性 ,还有个参数,data= 可以写入一些给后端的字符串 46 // 匿名函数,函数会有getJSON创建随机字符串也就是callbacks=?的问好的值,函数参数的data是数据 47 $.getJSON("http://127.0.0.1:8000/send_ajax/?callbacks=?",function (data) { 48 console.log(data) 49 }) 50 }) 51 52 </script> 53 54 55 {#// ==========================================基于jquery的JSONP的实现2#} 56 57 <script> 58 59 $(".send_ajax").click(function () { 60 61 $.ajax({ 62 url:"http://127.0.0.1:8000/send_ajax/", 63 success:function (data) { 64 alert(data) 65 }, 66 // 跨域请求 <script> 告诉服务器我要什么类型的数据contenttype是告诉服务器我是什么类型数据 67 dataType:"jsonp", 68 jsonp: 'callbacks', //?callbacks=SayHi 相当于这个,问题就是在于前后端的函数名要相同 69 jsonpCallback:"SayHi" 70 }) 71 72 73 }); 74 75 function SayHi(arg) { 76 console.log("hello",arg) 77 } 78 </script> 79 80 {#// ==========================================基于jquery的JSONP的实现3 (推荐)#} 81 82 <script> 83 84 $(".send_ajax").click(function () { 85 86 $.ajax({ 87 url:"http://127.0.0.1:8000/send_ajax/", 88 dataType:"jsonp", // 跨域请求 <script> 89 // callbacks=? ?就是随机字符串了,后端传回来运行的就是success函数,也就是funciton 函数参数是数据 90 jsonp: 'callbacks', 91 success:function (data) { 92 console.log(data) 93 } 94 }) 95 96 }); 97 98 99 </script> 100 101 {#// =========================================应用#} 102 103 104 <script> 105 106 $(".send_ajax").click(function () { 107 108 $.ajax({ 109 url:"http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list&_=1454376870403", 110 dataType:"jsonp", 111 jsonp:"callback", 112 jsonpCallback:"list" 113 }) 114 115 116 }); 117 118 function list(shuju) { 119 console.log(shuju.data); // {data: Array(7)} 120 var data=shuju.data; // [{},{},{},......] 121 122 $.each(data,function (i,weekend) { 123 //console.log(weekend); // {week: "周日", list: Array(19)} 124 console.log(weekend.list); // {week: "周日", list: Array(19)} 125 var week= weekend.week; 126 $("body").append("<div>"+week+"</div>"); 127 128 $.each(weekend.list,function (i,show) { 129 console.log(show); // {time: "1800", name: "《都市现场》90分钟直播版块", link: "http://www.jxntv.cn/live/jxtv2.shtml"} 130 131 var $a=$("<a>"); // <a></a> 132 $a.html(show.name); 133 $a.attr("href",show.link); 134 135 $("body").append('<p>'); 136 $("body").append($a); 137 $("body").append('</p>'); 138 139 }) 140 141 142 }) 143 144 } 145 </script> 146 </body> 147 </html>
注意 JSONP一定是GET请求
CORS跨域资源共享(CORS,Cross-Origin Resource Sharing)
其本质是设置响应头,使得浏览器允许跨域请求。
项目一要访问项目二的视图,项目二的ajax请求代码如下
1 <h1>项目2的首页cors</h1> 2 3 <button class="send_ajax">send_ajax</button> 4 5 <script> 6 7 $(".send_ajax").click(function () { 8 9 $.ajax({ 10 // 这里我访问s1的的send_ajax 11 url:"http://127.0.0.1:8000/send_ajax/", 12 type:'GET', // head get post put delete 13 success:function (data) { 14 console.log(data); 15 console.log('11') 16 } 17 }) 18 19 }) 20 </script>
项目二通过路由找到视图返回数据的代码如下
1 import json 2 def send_ajax(request): 3 # 授权的origin可以用*代替所有,methods可以多个,用逗号分开,简单的放一起,复杂放一起 4 if request.method == "OPTIONS": # 预检 5 response = HttpResponse() 6 7 response["Access-Control-Allow-Origin"] = "http://127.0.0.1:8011" 8 response["Access-Control-Allow-Methods"] = "PUT" 9 10 return response 11 12 elif request.method == "PUT": # 复杂请求,需要通过预检 put delete 13 s = {"name": "egon", "age": 122} 14 15 response = HttpResponse(json.dumps(s)) 16 response["Access-Control-Allow-Origin"] = "http://127.0.0.1:8011" 17 18 return response 19 20 elif request.method == 'GET': # 简单强求 head get post 21 s = {"name": "egon", "age": 122} 22 23 response = HttpResponse(json.dumps(s)) 24 response["Access-Control-Allow-Origin"] = "http://127.0.0.1:8011" 25 26 return response
流程:
项目二向项目一发送请求,项目一根据请求匹配路由规则,找到视图,视图首先返回给浏览器,如果没有cors就会被浏览器拦截,有了cors设置,浏览器就不会拦截cors设置的请求方式,最终
返回给项目一的数据,上述例子中,复杂请求(put,delete)先走预检,添加put请求,和允许访问的url,返回给浏览器,浏览器得到后在向服务器发送put请求,拿到数据,最后给项目一。