美文网首页
ajax跨域问题(python版本)

ajax跨域问题(python版本)

作者: warmsirius | 来源:发表于2019-08-17 01:25 被阅读0次

一、为什么会出现跨域

跨域问题来源于JavaScript的同源策略,即只有 协议+主机名+端口号 (如存在)相同,则允许相互访问。即JavaScript只能访问和操作自己域下的资源,不能访问和操作其他域下的资源。

跨域问题是针对JS和ajax的,html本身没有跨域问题,比如a标签、script标签、甚至form标签(可以直接跨域发送数据并接收数据)

注意:localhost和127.0.0.1也属于跨域。

1. 跨域产生的原理

  • 当向不同源的资源发起Ajax请求时, 浏览器会加上Origin字段来标识源
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
Content-Length: 8
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Host: localhost:7070
Origin: http://localhost:9090   协议+域名+端口
  • 服务器会根据Origin字段决定是否同意这次请求

如果Origin指定的源不在许可范围内, 服务器会返回一个不带有Access-Control-Allow-Origin字段的响应. 浏览器解析时发现缺少了这个字段, 就会报错.

注意:这种错误不能通过状态码识别, 因为状态码很有可能就是200.

解决方案一、在服务端添加响应头Access-Control-Allow-Origin

前面说明了,Ajax跨域失败是因为响应中缺少了响应头Access-Control-Allow-Origin, 那么就想办法加上去.

修改Django中的views.py文件修改views.py中对应API的实现函数,给返回值加上响应头Access-Control-Allow-Origin,允许其他域通过Ajax请求数据:

区分简单的请求和复杂的请求

简单请求:

满足以下两个条件的请求。

(1) 请求方法是以下三种方法之一:

  • 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

复杂请求:

非简单请求就是复杂请求。

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。

非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。

预检请求为OPTIONS请求,用于向服务器请求权限信息的。

预检请求被成功响应后,才会发出真实请求,携带真实数据。

a. 简单的请求只需要加上"Access-Control-Allow-Origin"

 def myview(_request):  
    response = HttpResponse(json.dumps({"key": "value", "key2": "value"}))
    response["Access-Control-Allow-Origin"] = "*"  #也可换成想要访问的域名
    response["Access-Control-Allow-Methods"] = "POST, GET, OPTIONS"  
    response["Access-Control-Max-Age"] = "1000"  
    response["Access-Control-Allow-Headers"] = "*"  
    return response 

b. 复杂的请求需要设置两步

  • 服务器端后台接口允许OPTIONS请求,如果是非简单的请求方法,需要设置请求访问的方式
  • 后端增加对应的头部支持
# DELETE方法下的跨域
def myview(request):
    if request.method == 'OPTION':
        res = HttpResponse()
        response["Access-Control-Allow-Methods"] = " DELETE"  
        response["Access-Control-Allow-Headers"] = "*"  
        return res
    res = HttpResponse()
    response["Access-Control-Allow-Headers"] = "*"  
    return res

解决方法二、利用JSONP

JSONP是JSON with Padding的略称。它是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)。

1. JSONP实现流程

JSONP的实现步骤大致如下(参考了来源中的文章)

  • i. 客户端网页网页通过添加一个<script>元素,向服务器请求JSON数据,这种做法不受同源政策限制
function addScriptTag(src) {
  var script = document.createElement('script');
  script.setAttribute("type","text/javascript");
  script.src = src;
  document.body.appendChild(script);
}

window.onload = function () {
  addScriptTag('http://example.com/ip?callback=foo');
}

function foo(data) {
  console.log('response data: ' + JSON.stringify(data));
};                      

请求时,接口地址是作为构建出的脚本标签的src的,这样,当脚本标签构建出来时,最终的src是接口返回的内容

  • ii. 服务端对应的接口在返回参数外面添加函数包裹层
foo({
  "test": "testData"
});               
  • iii. 由于<script>元素请求的脚本,直接作为代码运行。

这时,只要浏览器定义了foo函数,该函数就会立即调用。作为参数的JSON数据被视为JavaScript对象,而不是字符串,因此避免了使用JSON.parse的步骤。

注意: 一般的JSONP接口和普通接口返回数据是有区别的,所以接口如果要做JSONP兼容,需要进行判断是否有对应callback关键字参数,如果有则是JSONP请求,返回JSONP数据,否则返回普通数据

JSONP使用注意

基于JSONP的实现原理,所以JSONP只能是“GET”请求,不能进行较为复杂的POST和其它请求,所以遇到那种情况,就得参考下面的CORS解决跨域了(所以如今它也基本被淘汰了)。

2. 基于JS的JSONP的实现

前面讲了JSONP的实现原理,现在我们可以自己写JS来实现JSONP功能。

一般情况下,我们希望这个script标签能够动态的调用,而不是像固定在html里面所以没等页面显示就执行了,很不灵活。

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

#项目1中html部分代码
<script>
    //回调函数
    function bar(data) {
        alert(data);
        console.log(JSON.parse(data))
    }
</script>

<script>
    //触发事件生成script标签
    $(".ajax_btn").click(function () {
        script_request("http://127.0.0.1:8002/send_ajax/?callback=bar") //请求路径带回调函数名

    });
    //定义一个生成script标签的函数
    function script_request(url) {
        var $script=$("<script>");
        $script.attr("src",url);
        $("body").append($script);
        $("body script:last").remove()

    }
</script>

#项目2中的视图函数,即上述script标签请求的路径
def send_ajax(request):
    import json
    func_name=request.GET.get("callback") #获得回调函数的名字
    dic={"k1":"v1"}
    return HttpResponse("%s('%s')" %(func_name,json.dumps(dic)))

为了更加灵活,上述我们将你自己在客户端定义的回调函数的函数名传送给服务端,服务端则会返回以你定义的回调函数名的方法,将获取的json数据传入这个方法完成回调。

3. 基于jquery的JSONP的实现: $.getJSON()

/项目1中html中部分代码
<script>
    $(".ajax_btn").click(function () {
        $.getJSON("http://127.0.0.1:8002/send_ajax/?callback=?",function (data) {
            alert(data);
            console.log(data)
        })

    });
</script>
//项目2中的视图函数,即跨域请求的路径
def send_ajax(request): 
  import json 
  func_name=request.GET.get("callback") #获得回调函数的名字 
  dic={"k1":"v1"} print("ok") 
  return HttpResponse("%s('%s')" %(func_name,json.dumps(dic)))

如上,jQuery框架也当然支持JSONP,可以使用 $.getJSON(url,[data],[callback]) 方法。

与js实现的方式相比,我们并不要自己生成一个script标签,客户端也并不需要自己定义一个回调函数.

注意: 在$.getJSON(url,[data],[callback])方法的url的后面必须添加一个callback参数,这样getJSON方法才会知道是用JSONP方式去访问服务,callback后面的那个问号是内部自动生成的一个回调函数名。

4. 基于jquery的JSONP的实现:dataType: JSONP

上述这种方法,很方便,不需要我们自己定义回调函数和指定回调函数名,但是,如果说我们想指定自己的回调函数名,或者说服务上规定了固定回调函数名该怎么办呢?

我们可以使用$.ajax方法来实现。如下例:

//项目1中html中部分代码
<script>
   $(".ajax_btn").click(function () {
      $.ajax({
          url:"http://127.0.0.1:8002/send_ajax/",
          dataType:"jsonp",                 //相当于script标签
          jsonp:'callbacks',                //相当于路径中回调函数路径参数键值对的键
          jsonpCallback:"func"              //相当于路径中回调函数路径参数键值对的值,回调函数名

      })

   });
   //定义回调函数
   function func(arg) {
       alert(arg);
       console.log(arg)

   }
</script>

//项目2中的视图函数,即跨域请求的路径
def send_ajax(request):
   import json
   func_name=request.GET.get("callbacks")    #获得回调函数的名字
   dic={"k1":"v1"}
   print("ok")
   return HttpResponse("%s('%s')" %(func_name,json.dumps(dic)))

5. 基于jquery的JSONP的实现(三)

在上小节中jsonp: 'callbacks'就是定义一个存放回调函数的键,jsonpCallback是前端定义好的回调函数方法名,server端接受callback键对应值后就可以在其中填充数据打包返回。

但是,jsonpCallback参数可以不定义,jquery会自动定义一个随机名发过去,那前端就得用回调函数来处理对应数据了。利用jQuery可以很方便的实现JSONP来进行跨域访问。

//项目1中html中部分代码
<script>
    $(".ajax_btn").click(function () {
       $.ajax({
           url:"http://127.0.0.1:8002/send_ajax/",
           dataType:"jsonp",
           jsonp:'callbacks',
           success:function (data) {
               alert(data);
               console.log(data)
           }
       })
    });
</script>

//项目2中的视图函数,即跨域请求的路径
def send_ajax(request):
    import json
    func_name=request.GET.get("callbacks")    #获得回调函数的名字
    dic={"k1":"v1"}
    print("ok")
    return HttpResponse("%s('%s')" %(func_name,json.dumps(dic)))

解决办法三、使用Django提供的 Django-cors-headers 来处理跨域

  • 从GitHub上面下载Django-cors-headers

  • pip install Django-cors-headers.zip

  • 在settings.py中的中间件中配置 【'corsheaders.middleware.CorsMiddleware',】

注意: 这个中间件一定要写在CSRF之前,为了方便处理,一般写在最前面.

  • 设置 CORS_ORIGIN_ALLOW_ALL = True,即允许所有的跨域请求,当然,这里也可以设置为False,然后配合 CORS_ORIGIN_WHITELIST 白名单来使用
INSTALLED_APPS = [
    ...
    'corsheaders',
    ...
 ] 

MIDDLEWARE_CLASSES = (
    ...
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware', # 注意顺序
    ...
)
#跨域增加忽略
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_WHITELIST = (
    '*'
)

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',
)

如此,我们的跨域处理即完成,支持所有的请求。

相关文章

  • ajax跨域问题(python版本)

    一、为什么会出现跨域 跨域问题来源于JavaScript的同源策略,即只有 协议+主机名+端口号 (如存在)相同,...

  • 解决ajax跨域问题

    Jsonp解决ajax跨域问题 CORS解决ajax跨域问题

  • SpringMVC 进行ajax跨域请求访问

    关于 springmvc 3.x 版本对ajax跨域请求访问 ajax 请求后,浏览器出现跨域的问题那么在当前环境...

  • Http浅析【2】——ajax跨域问题

    视频参考:ajax跨域完全讲解 本文精华版:【综合】ajax跨域问题 什么是跨域问题 简单来讲,当前台调用后台,如...

  • 前端跨域

    什么是ajax跨域 ajax跨域的原理 ajax出现请求跨域错误问题,主要原因就是因为浏览器的“同源策略”,可以参...

  • ajax跨域请求

    ajax跨域请求(jsonp) 利用JSONP解决AJAX跨域问题的原理与jQuery解决方案JSONP jQue...

  • window.postMessage解决前端ajax跨域问题

    postMessage畅快解决跨域问题 本文主要是记录使用window.postMessage解决ajax跨域问题...

  • 使用JSONP解决ajax跨域

    在日常开发中,不免遇到跨域的问题。在这里我们介绍使用Jsonp来解决ajax跨域的问题 什么是跨域? 跨域,指的是...

  • Flask-cors跨域

    什么是跨域 为什么要考虑跨域问题 同源策略 解决跨域问题 方式一: 使用 JSONP (一种非Ajax技术,需要前...

  • 解决跨域问题

    概述 在浏览器端进行 Ajax 请求时会出现跨域问题,那么什么是跨域,如何解决跨域呢?先看浏览器端出现跨域问题的现...

网友评论

      本文标题:ajax跨域问题(python版本)

      本文链接:https://www.haomeiwen.com/subject/cdlcsctx.html