跨域请求
Posted MISF
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了跨域请求相关的知识,希望对你有一定的参考价值。
源自:http://www.cnblogs.com/yuanchenqi/articles/5997456.html
一.同源策略jsonp实现
1.同源策略机制
所谓同源是指域名,协议,端口相同,浏览器界面A想访问界面B,就要保证同源。如http://127.0.0.1:8888
我们需要注意一点:在同源机制下,如果要写jQuery,就要先引入<script src=" xxx/xxx/xx.js"></script>,这里面包含的src属性(凡是带有src属性的都可以实现跨域访问)和当前网页是不同源的,jsonp正是利用这一点实现跨域访问。Ajax本身不能实现跨域访问,是通过jsonp实现的。
2.jsonp(json+padding,padding是一个填充,在jsonp方式中,服务端返回的一定是A(a),A为函数名,a为填充对象。jsonp只能通过get请求)
jsonp原理:动态创建script标签,内有src属性(src属性不受同源机制,注意:一旦创建srcipt带有src属性的标签,就会自动执行),实现跨域访问,然后删除srcipt标签。
2.1 jsonp的js实现
同一个Django文件,分别用8000端口和8002端口打开。浏览器输入127.0.0.1:8000/login,返回8000端口的index.html界面,然后通过src在进入到8002端口的get_byjsonp函数
----------index.html
<script> function fun1(arg){ alert("hello"+arg) } </script> <script src="http://127.0.0.1:8002/get_byjsonp/"></script> //返回:<script>fun1("yuan")</script>,执行fun1("yuan")函数
---------------views.py
def login(req): print("hrllo ajax") return render(req,"index.html") def get_byjsonp(req): print("8002") return HttpResponse("fun1(\'yuan\')") //jsonp对象
---------------urls.py
urlpatterns = [ path(\'admin/\', admin.site.urls), path(\'login/\',views.login), path(\'get_byjsonp/\',views.get_byjsonp), ]
2.2 jsonp的js动态创建
-------------index.html
<button onclick="f()">submit</button> <script> function addScriptTag(src){ //动态创建script标签 var script = document.createElement(\'script\'); //创建标签<script> script.setAttribute("type","text/javascript"); //设置属性<script type="text/javascript"> script.src = src; // document.body.appendChild(script); //</script> document.body.removeChild(script); } function fun1(arg){ alert("hello"+arg) } function f(){ addScriptTag("http://127.0.0.1:8002/get_byjsonp/")//返回:<script>fun1("yuan")</script>,执行fun1("yuan")函数
} </script>
---------------views.py
def login(req): print("hrllo ajax") return render(req,"index.html") def get_byjsonp(req): print("8002") return HttpResponse("fun1(\'yuan\')")//jsonp对象
2.3 (callback=...键值对传给服务端)
------------------index.html
<button onclick="f()">submit</button> <script> function addScriptTag(src){ //得到<script>SayHi("yuan")</script> 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",None) //得到SayHi函数名 return HttpResponse("%s(\'yuan\')"%func)
3.1 jsonp的jQuery实现
---------------index.html
<script type="text/javascript"> $.getJSON("http://127.0.0.1:8002/get_byjsonp?callback=?",function(arg){ //callback=?中的?是一堆看不懂的字符串 alert("hello"+arg) }); </script>
------------------views.py
def login(req):
print("hrllo ajax")
return render(req,"index.html")
def get_byjsonp(req):
print("8002")
callback=req.GET.get("callback",None) //callback=“xxxxxxx”
return HttpResponse("%s(\'ff\')"%callback)
4.1 jsonp的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" //和上边一句合成键值对callbacks="SayHi"发送给服务端 }); function SayHi(arg){ alert(arg); } </script> #--------------------------------- views.py def get_byjsonp(req): callback=req.GET.get(\'callbacks\') //得到函数名SayHi print(callback) return HttpResponse(\'%s("yuan")\'%callback)
4.2 jsonp的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", //必须有,告诉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)
二.CORS(跨域资源共享)
浏览器将cors分为两类:简单请求和非简单请求
满足以下条件,就是简单请求
(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的。
简单请求和非简单请求的区别
简单请求:一次请求
非简单请求:两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用于数据传输。
* 关于“预检”
- 请求方式:OPTIONS
- “预检”就是做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息
- 如何“预检”
=> 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过
Access-Control-Request-Method
=> 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过
Access-Control-Request-Headers
简单请求跨域
创建2个django项目,项目pro1用8000端口开启,项目pro2用8002端口开启。
#urls.py from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r\'^admin/\', admin.site.urls), url(r\'^index/\',views.index), ] #views.py from django.shortcuts import render def index(req): ret = render(req,"index.html") return ret #index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script> </head> <body> 跨域 index <button id="btn">xx</button> <script> $("#btn").click(function () { $.ajax({ url:"http://127.0.0.1:8002/index/", #此处访问pro2的index路由 type:"put", success:function (data) { console.log(data); } }) }); </script> </body> </html>
#urls.py from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r\'^admin/\', admin.site.urls), url(r\'^index/\',views.index), ] #views.py from django.shortcuts import render def index(req): ret = render(req,"index.html") return ret #index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script> </head> <body> 跨域2 index </body> </html>
我们可以看到,通过pro1中的index界面点击button按钮,发送ajax请求pro2中的index界面,此时不同源,同时浏览器会报错
解决方法(修改header):在项目pro2中的views.py中如下写法
def put_json(request): response = HttpResponse("JSON复杂请求") if request.method == \'OPTIONS\': # 处理预检 response[\'Access-Control-Allow-Origin\'] = "*" response[\'Access-Control-Allow-Methods\'] = "PUT" return response elif request.method == "PUT": return response
如果是非简单请求(在项目pro1中将ajax请求方式改为put),会先发送OPTION请求,在发送PUT请求
同时浏览器会报错
解决方法:在项目pro2的views视图函数中
def index(req): ret = render(req,"index.html") ret["Access-Control-Allow-Origin"] = "http://127.0.0.1:8000" ret["Access-Control-Allow-Methods"] = "PUT" #设置非同源的PUT请求能通过 return ret
以上是关于跨域请求的主要内容,如果未能解决你的问题,请参考以下文章
Express实战 - 应用案例- realworld-API - 路由设计 - mongoose - 数据验证 - 密码加密 - 登录接口 - 身份认证 - token - 增删改查API(代码片段