1-Django - 解决跨域的三种方式

环境:python3.6.2+Django1.11.5+pycharm2017.2.3


首先了解什么是浏览器同源策略?

1995年,同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策。

最初,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页"同源"。所谓"同源"指的是"三个相同":

  • 协议相同
  • 域名相同
  • 端口相同

不受浏览器同源策略影响的有,img,script,iframe,它们通过其src属性去请求数据,绕过浏览器同源策略,

所以,ajax也受限,但有些时候,我们不得不做一些手脚来达到ajax跨域请求操作,那么,就用到jsonp和cors了。

注意:cors和jsonp依赖于jquery,使用之前需要导入jquery。

jsonp方式:

我们在client端向server端发送一个普通的ajax请求,结果,会报一个:Access-Control-Allow-Origin的错误,但我们查看server端,是已经反回了,只是数据被浏览器拦截了,那么我们利用script的src的属性进行请求,

我们手动创建一个script标签, 传递URL,定义发送函数和接收变量的函数,但仅仅是这样的话,不做其他操作的话,会报错:数据未定义,因为,通过script请求过来的数据,会按照script的规则当做变量进行定义,所以,数据被script当做变量,导致报错

那么,知道如何报错,我们就手动给变量定义,callback=func,但是,这要server端配合对数据进行配合,不够灵活,因为两边的callback都要一致,client端变一下callback,还要告诉server端同步更改,为了解决这个,我们对server端更改代码,如示例所示,获取到client端的callback,再进行字符串格式化,这样,我们做好如此更改,不管client传什么,我们就可以一劳永逸了

这样,我们手动的实现了jsonp的原理

For exampl:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
</head>
<body>

<h1>ajax跨域之jsonp</h1>

<input type="button" id="b1" value="向自己家发送请求">
<input type="button" id="b2" value="向别的网址发送请求">
<input type="button" id="b3" value="手动实现jsonp本质">
<input type="button" id="b4" value="手动实现jsonp本质callback">
<input type="button" id="b5" value="ajax的jsonp,自己写同名回调函数">
<input type="button" id="b6" value="ajax的jsonp,一切从简">

<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
    // 向自己家发送请求没问题,
    $("#b1").click(function () {
        $.ajax({
            url: 'http://127.0.0.1:8844/test1/',
            type: 'GET',
            success: function (arg) {
                alert(arg)
            }
        })
    });

    // 咱们向别的网址发送请求就会遇到问题,浏览器同源策略影响
    $("#b2").click(function () {
        $.ajax({
            url: 'http://127.0.0.1:8033/data/',
            type: 'GET',
            success: function (arg) {
                alert(arg)
            }
        })
    });

    /*
    jsonp的的本质,利用script标签的src属性发送请求
    script的src属性把返回的数据当成一个未定义的变量,所以,我们还要对这个变量做处理
    */
    $("#b3").click(function () {
        var tag = document.createElement('script');
        tag.src = 'http://127.0.0.1:8033/data2/';
        document.head.appendChild(tag)
    });

    // 利用自定义函数来获取返回的数据

    function list(arg) {
        alert(arg);
        document.head.removeChild(tag)
    }

    $("#b4").click(function () {
        tag = document.createElement('script');
        tag.src = 'http://127.0.0.1:8033/data3/?callback=list';
        document.head.appendChild(tag)
    });


    // ajax的jsonp,这两种方式,差不多,请求的网站只需动态加上回调函数
    //方式一,在自定义的同名回调函数里处理数据,虽然success里也可以处理

    function dict(arg) {
        alert(arg);
    }

    $("#b5").click(function () {
        $.ajax({
            url: 'http://127.0.0.1:8033/data5/',
            type: 'GET',
            dataType:'JSONP', // 传输方式是jsonp
            jsonp:'callback', // 等于在地址栏后面加上:?callback
            jsonpCallback:'dict', // 等于http://127.0.0.1:8033/data4/?callback=dict
        })
    });

    // 方式二,一切从简,直接在ajax的success里处理数据
    // 要是单独只说明datatype,则ajax会自己定义callback,但是所请求的网站包含数据的函数名,不能写死
    // 需写动态的,因为ajax的callback是动态的,

    $("#b6").click(function () {
        $.ajax({
            url: 'http://127.0.0.1:8033/data5/',
            type: 'GET',
            dataType:'JSONP',
            success: function (arg) {
                alert(arg)
            }
        })
    });


</script>

</body>
</html>
jsonp原理示例
from django.shortcuts import render,HttpResponse

# Create your views here.


def data(request):
    """
    正常返回,不要对数据做处理
    """
    return HttpResponse('数据66666')


def data2(request):


    # 直接返回没有问题,但对方把数据当成未定义的变量
    # return HttpResponse('数据66666')
    # 所以,我们要配合,比如下面这样, 但这样太复杂了
    return HttpResponse('alert(111111)')

def data3(request):
    import time
    time.sleep(2)
    data_obj = 'qwertytffasdf'
    response = request.GET.get('callback')
    print(response)
    return HttpResponse('%s("%s")'%(response,data_obj))

def data5(request):

    data_obj = 'aaaaaaaaaaaaaaaa'
    response = request.GET.get('callback')
    print(response)
    return HttpResponse('%s("%s")' % (response, data_obj))
    # return HttpResponse('func("66666666")')
server端手动配合

jsonp本质:

   虽然依然通过ajax发送请求,但jsonp却偷偷地通过创建一个script标签进行数据的获取

   过程是:在head头内建立script标签,获取到数据之后,立即把script标签删掉

注意:

  如果在发送ajax时,只指定dataType:‘JSONP’,那么, jquery会自动的添加callback,如上面jsonp原理示例里面的方式二所示,我们无需再次定义一个函数去处理数据,直接在success里处理,如果按照方式一,可以不用写success这个回调函数,但是,还是推荐方式二,代码简洁;

问题:jsonp是否支持post?

  不支持

jsonp只能发get请求

CORS(Cross-Origin Resource Sharing):跨站资源共享

cors实现思想:

  CORS背后的基本思想是使用自定义的HTTP头部允许浏览器和服务器相互了解对方,从而决定请求或响应成功与否

简单请求&&复杂请求

简单请求 OR 非简单请求 
条件: 
    1、请求方式:HEAD、GET、POST 
    2、请求头信息: 
        Accept Accept-Language 
        Content-Language 
        Last-Event-ID 
        Content-Type 对应的值是以下三个中的任意一个 
            application/x-www-form-urlencoded 
            multipart/form-data 
            text/plain 
注意:同时满足以上两个条件时,则是简单请求,否则为复杂请求 
简单请求和非简单请求的区别? 
简单请求:
    一次请求 
非简单请求:
    两次请求,在发送数据之前会先发一次请求用于做“预检”,
    只有“预检”通过后才再发送一次请求用于数据传输。 
关于“预检” 
    - 请求方式:OPTIONS 
    - “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息 
    - 如何“预检” 
        如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过 
        Access-Control-Request-Method="PUT" 
        如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过 
        Access-Control-Request-Headers="k1"
简单请求 OR 非简单请求

基于cors实现的ajax请求:

1、支持跨域的简单请求

  服务器设置响应头:Access-Control-Allow-Origin = '指定域名' 

  服务器设置响应头:Access-Control-Allow-Origin = '*'    # *:表示允许所有的请求

2、复杂请求的请求头:

  由于复杂请求时,首先会发送“预检”请求,如果“预检”成功,则发送真实数据。   # 预检时可以发送空数据

  “预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method="PUT"    # 或其他复杂请求如DELETE

  “预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers="k1"     # ajax的header:{"k1":"v1"}

  “预检”缓存时间,服务器设置响应头:Access-Control-Max-Age=10   # 十秒内此请求再有请求过来时无需再发送options预检,直接发送复杂请求,减轻服务器压力

3、跨域获取响应头

  默认获取到的所有响应头只有基本信息,如果想要获取自定义的响应头,则需要再服务器端设置Access-Control-Expose-Headers。

4、跨域传输cookie

  在跨域请求中,默认情况下,HTTP Authentication信息,Cookie头以及用户的SSL证书无论在预检请求中或是在实际请求都是不会被发送。

  如果想要发送:

    浏览器端:XMLHttpRequest的withCredentials为true

    服务器端:Access-Control-Allow-Credentials为true

注意:服务器端响应的 Access-Control-Allow-Origin 不能是通配符

def cors(request):
    if request.method == 'OPTIONS':
        response = HttpResponse()
        # 设置缓存时间,在请求的十秒内,无需发送options请求预检验证,
        response['Access-Control-Max-Age'] = 10
        # 只允许这个请求访问
        # response['Access-Control-Allow-Origin'] = "http://127.0.0.1:8844"
        # 允许所有请求来访问
        response['Access-Control-Allow-Origin'] = "*"

        # 允许那些复杂的请求来访问
        response['Access-Control-Allow-Methods'] = "PUT,DELETE"
        return response
    else:
        response = HttpResponse('CORS数据')
        response['Access-Control-Allow-Origin'] = "http://127.0.0.1:8844"
        return response
响应头

 

如果要处理多个请求的跨域,可以在中间件内处理

from django.utils.deprecation import MiddlewareMixin


class CorsMiddleware(MiddlewareMixin):
    """
    为解决跨域问题,所建立的中间件
    """

    def process_response(self, request, response):
        """
        :param response: server端向client端返回的json数据
        :return: 添加响应头
        """
        response["Access-Control-Allow-Methods"] = "POST,GET,OPTIONS"
        response['Access-Control-Allow-Origin'] = '*'
        response["Access-Control-Max-Age"] = '100000'
        response['Access-Control-Allow-Headers'] = "content-type"
        return response
corsmiddleware

 

requests:

另外的:requests模式:

导入Python的requests模块

requests.get("URL")

requests模式不受任意影响就能发送获取请求

总结:

三者的优缺点:

  • cors相比jsonp支持更多的请求方式,
  • jsonp需要client和server端的相互配合,
  • jsonp比cors方式兼容性更好
  • cors在client端无需设置,server端需要针对不同的请求,来做head头的处理
  • jsonp和cors都是在本地执行请求数据,因为打开网页的时候,已经将js代码下到本地浏览器了,然后浏览器负责接下来的操作
  • 而requests模式,则是浏览器向服务器发送请求,服务器根据请求,去拿数据,然后再返回给浏览器,增加服务器压力

 

 

 

测试代码:https://github.com/TestXX11/jsonp-cors

参考博客:http://www.cnblogs.com/wupeiqi/articles/5703697.html

参考博客:http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html

posted @ 2017-10-18 15:04  听雨危楼  阅读(2983)  评论(0编辑  收藏  举报