Django框架中JSONP跨域请求的本质以及解决方式

首页 » Python教程 » Django框架中JSONP跨域请求的本质以及解决方式

为了更好的解释JSONP跨域请求的本质,我们先通过Python的爬虫请求模块requests,来看一下有什么效果。requests模块的功能就是请求url然后返回response信息,我们还是使用Django来实现。创建req路由规则,通过requests.get向某个数据API接口发送请求,并返回response,将response.text文本展示在req的url中,具体实现:

import requests
def req(request):
    response=requests.get('https://www.iconfont.cn/api/common/indexConfig.json?t=1588556274895&ctoken=B4ftU3SA5676hkHnZaBJRi9f')
    html=response.content.decode()
    return render(request,'req.html',{'html':html})

以上代码请求的是一个iconfont图标的api接口,html是接口的数据,我们传递到req.html页面中并展示出来,其它地方自行配置好,那么我们可以看到没什么问题,数据呈现在网页中,并且浏览器的控制台也没有错误信息。

这种方式是通过requests去请求数据接口,然后放到我们的django服务端。我们试试直接通过django的服务器去获取数据,会发生什么。接下来在req.html页面搞个按钮,并绑定js事件,点击就会访问api接口:

<input type="submit" onclick="getContent();" value="js获取">
<script>
    function getContent() {
        var xhr=new XMLHttpRequest();
        xhr.open('GET','https://www.iconfont.cn/api/common/indexConfig.json?t=1588556274895&ctoken=B4ftU3SA5676hkHnZaBJRi9f');
        xhr.onreadystatechange=function(){
            conslole.log(xhr.responseText)
        };
        xhr.send();
    }
</script>

现在运行django服务端,然后点按钮请求数据,那么就成功的触发了下图的报错信息:

Django框架中JSONP跨域请求的本质以及解决方式

英文报错:No ‘Access-Control-Allow-Origin’ header is present on the requested resource.,翻译过来就是已被CORS策略阻止:请求的资源上不存在“Access Control Allow origin”头

其实我们呢在点击按钮的时候已经成功的向数据接口发送信息,并且对方已经吧数据给我们了,但是被浏览器拒绝了,这是由于浏览器的同源策略,它不允许你向其它域名发送ajax请求。鳄鱼君Ba提供了一下三种方式来解决:

通过jsonp处理跨域(基于原生js)

注意:jsonp只能模拟get请求处理跨域

1、jsonp介绍

jsonp是json用来跨域的一个东西。原理是通过script标签的跨域特性来绕过同源策略。下面介绍一个jsonp基本实现原理,即利用script特性实现跨域请求,如下实例:

2、基于JS的JSONP的实现

一般情况下,我们希望这个script标签能够动态的调用,我们可以通过页面的触发事件操作后,通过javascript动态的创建script标签,这样我们就可以灵活调用远程服务。实例如下:

前面的两种方式进行对比,你就会发现真的是浏览器阻止了对方返回的数据。第一种是使用requests请求数据接口,返回并展示在浏览器中,浏览器只是参与了展示信息;第二种是通过ajax的xhr对象发送请求,这个过程了是借助浏览器来发送请求的,但是数据被阻止了。解决办法很简单,浏览器会阻止Ajax请求,但是不会阻止通过script标签发送的请求,这是前辈们说的,我也不知道,所以试试看:

<input type="submit" onclick="getContent();" value="js获取">
<script>
    function getContent() {
        var tag=document.createElement('script'); //创建script标签
        tag.src='http://e1yu.com:8000/res/?k1=name&k2=age'; //设置src属性
        document.head.appendChild(tag); //将script标签添加到head中
        document.head.removeChild(tag);//将script标签移除
    }

</script>

修改代码后运行django服务端,那么又一次非常开心的看到浏览器又给了一些提示Cross-Origin Read Blocking (CORB) blocked cross-origin response https://xxx with MIME type application/json.(跨源读取阻塞(CORB)用MIME类型application/json阻塞了跨源响应https://xxx。),前面其实还有一个错误:A cookie associated with a cross-site resource at https://www.iconfont.cn/ was set without the `SameSite` attribute. A future release of Chrome will only deliver cookies with cross-site requests if they are set with `SameSite=None` and `Secure`. (与https://www.iconfont.cn/上的跨站点资源关联的cookie未设置“SameSite”属性。如果Chrome的未来版本设置为“SameSite=None”和“Secure”,则它只能传递带有跨站点请求的cookie)。

<input type="submit" onclick="getContent();" value="js获取">
<script>
    function sendMsg2() {
        var tag = document.getContent('script');
        tag.src = 'http://127.0.0.1:8001/api/?callback=list';
        document.head.appendChild(tag);

        document.head.removeChild(tag);
    }

    // 动态回调函数
    function list(arg) {
        console.log(arg);
    }
</script>

为了更加灵活,可以自己在客户端定义一个回调函数,然后将函数名传送给服务端,服务端则会返回以你定义的回调函数名的方法,将获取的json数据传入这个方法完成回调。这里你需要使用django创建两个服务端才可以。一般来说所有的网站都是这样使用的,你可以自己尝试一下。

from django.shortcuts import render, HttpResponse

def api(request):
    # 获取查询字符串中的callback参数
    func_name = request.GET.get('callback')
    return HttpResponse('%s("baidu.com")' % func_name)

通过jsonp处理跨域(基于jQuery中的getJSON方法)

基于jQuery框架也当然支持JSONP,可以使用$.getJSON(url,[data],[callback])方法。与js实现的方式相比,我们并不要自己生成一个script标签,客户端也并不需要自己定义一个回调函数,要注意的是在$.getJSON(url,[data],[callback])方法的url的后面必须添加一个callback参数,这样getJSON方法才会知道是用JSONP方式去访问服务,callback后面的那个问号是内部自动生成的一个回调函数名。

<input type="submit" class="ajax_btn" value="getjson获取">
<script>
    $(".ajax_btn").click(function () {
        $.getJSON("http://127.0.0.1:8001/api/?callback=list",function (data) {
            console.log(data);
        })

    });
</script>

项目中的视图函数,即跨域请求的路径
def res(request): 
  import json 
  func_name=request.GET.get("callback") #获得回调函数的名字 
  return HttpResponse("%s('123')" % func_name)

通过jsonp处理跨域(基于jQuery中的ajax方法)

上述这种方法,很方便,不需要我们自己定义回调函数和指定回调函数名,但是,如果说我们想指定自己的回调函数名,或者说服务上规定了固定回调函数名该怎么办呢?我们可以使用$.ajax方法来实现。如下例:

<input type="submit" class="ajax_btn" value="getjson获取">
<script>
    $(".ajax_btn").click(function () {
       $.ajax({
           url:"http://127.0.0.1:8001/api/",
           dataType:"jsonp",     //相当于script标签
           jsonp:'callbacks',    //相当于路径中回调函数路径参数键值对的键
           jsonpCallback:"func"  //相当于路径中回调函数路径参数键值对的值,回调函数名
       })
    });
    //定义回调函数
    function func(arg) {
        console.log(arg);
    }
</script>

项目中的视图函数,即跨域请求的路径
def res(request):
    func_name=request.GET.get("callbacks")    #获得回调函数的名字
    return HttpResponse("%s('123')" % func_name)

django-cors-headers实现防跨域

测试失败…待更新呢
1.安装cors模块

pip install django-cors-headers

2.修改setting.py中配置

INSTALLED_APPS = [
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'corsheaders',
]

MIDDLEWARE = [
 'django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'corsheaders.middleware.CorsMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

3.添加允许访问的白名单,凡是出现在白名单的域名都可以访问后端接口

CORS_ORIGIN_WHITELIST = (
 '127.0.0.1:8080',
 'localhost:8080',
)

CORS_ALLOW_CREDENTIALS = True # 指明在跨域访问中,后端是否支持对cookie的操作。
CORS_ALLOW_METHODS = (
 'DELETE',
 'GET',
 'OPTIONS',
 'PATCH',
 'POST',
 'PUT',
 'VIEW',
)

CORS_ALLOW_HEADERS = (
 'XMLHttpRequest',
 'X_FILENAME',
 'accept-encoding',
 'authorization',
 'content-type',
 'dnt',
 'origin',
 'user-agent',
 'x-csrftoken',
 'x-requested-with',
 'Pragma',
)

CorsMiddleware应该放置得尽可能高,特别是在可以产生响应的任何中间件之前, 如Django CommonMiddleware或Whitenoise WhiteNoiseMiddleware。 如果以前没有,则无法将CORS头添加到这些响应中。

暂且先整理,鳄鱼君Ba在django中测试的时候一直没有解决cors问题,待更新…….

未经允许不得转载:作者:鳄鱼君Ba, 转载或复制请以 超链接形式 并注明出处 鳄鱼君Ba
原文地址:《Django框架中JSONP跨域请求的本质以及解决方式》 发布于2020-05-04

分享到:
赞(0) 赏杯咖啡

评论 抢沙发

9 + 1 =


文章对你有帮助可赏作者一杯咖啡

支付宝扫一扫打赏

微信扫一扫打赏

Vieu4.6主题
专业打造轻量级个人企业风格博客主题!专注于前端开发,全站响应式布局自适应模板。
切换注册

登录

忘记密码 ?

您也可以使用第三方帐号快捷登录

Q Q 登 录
微 博 登 录
切换登录

注册