跨域请求

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>
pro1
#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>
pro2

我们可以看到,通过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(代码片段

如何发送跨域ajax请求[重复]

跨域请求如何携带cookie?不小心都拿了Offer

vue工程本地代码请求http发生跨域提示错误解决方法

VSCode自定义代码片段14——Vue的axios网络请求封装

VSCode自定义代码片段14——Vue的axios网络请求封装