• 跨域AJAX


    本篇主要讨论JSONP和CORS这两种技术,使用它们的原因是为了完成对资源的跨域访问,也就是如何绕过浏览器的同源策略Same-origin Policy

    那么什么是Same-origin Policy呢?简单地说,在一个浏览器中访问的网站不能访问另一个网站中的数据,除非这两个网站具有相同的Origin,也即是拥有相同的协议、主机地址以及端口。一旦这三项数据中有一项不同,那么该资源就将被认为是从不同的Origin得来的,进而不被允许访问。

    特别的:由于同源策略是浏览器的限制,所以请求的发送和响应是可以进行,只不过浏览器不接受罢了。

    浏览器同源策略并不是对所有的请求均制约:

    • 制约: XmlHttpRequest
    • 不叼: img、iframe、script等具有src属性的标签

    解决方案:

      1、-requests发请求时,跨域无限制

      2、- ajax发请求时,浏览器限制【是否可以绕过限制?】

          -JSONP

          -CORS

    requests模块当然可以通过跨域:

    1 #####服务端(urls.py)######
    2 
    3 urlpatterns = [
    4     url(r'^admin/', admin.site.urls),
    5     url(r'^get_data.html/$', views.get_data),
    6 ]
    1 ######服务端(views.py)######
    2 from django.shortcuts import render,HttpResponse
    3 
    4 def get_data(request):
    5     return HttpResponse(‘机密文件’)
    1 #####客户端(views.py)######
    2 
    3 def index(request):
    4     # 方式一:requests模块
    5     import requests
    6     response=requests.get("http://127.0.0.1:8000/get_data.html/")
    7     return render(request,"index.html",{"response":response})
     1 #####客户端(index.html)#######
     2 
     3 <!DOCTYPE html>
     4 <html lang="en">
     5 <head>
     6     <meta charset="UTF-8">
     7     <meta http-equiv="x-ua-compatible" content="IE=edge">
     8     <meta name="viewport" content="width=device-width, initial-scale=1">
     9     <title>Title</title>
    10     <script src="/static/jquery-3.2.1.js"></script>
    11 </head>
    12 <body>
    13 <h1>JIAのHOMESITE</h1>
    14{{ response.text }}
    15 </body> 

    16 </html>

    当然本篇主要讨论JSONP和CORS这两种跨域技术。

    一、JSONP

    依据:带有 src 属性的标签不受同源策略的影响(img, script, iframe等)。

    我们可以通过使用 script 标签来获取内容,但 script 中 src 获取内容后,获得到的字符串(类似于调用python中的 eval 或 exec)会被 JS 执行,所以我们可以定义一个函数,然后让服务端返回的内容外面包裹一层这个函数名,前端在访问的时候把这个函数名发送过去,并提前定义好该函数。

    当然,这种方法拥有一个显著的缺点,那就是只支持GET操作。

     

    手动实现:


    服务端返回的数据

    1 from django.shortcuts import render,HttpResponse
    2 
    3 
    4 def get_data(request):
    5     return HttpResponse('func("机密文件")')

    客户端定义及获取数据

    1 urlpatterns = [
    2     url(r'^admin/', admin.site.urls),
    3     url(r'^index.html/$', views.index),
    4     url(r'^cors.html/$', views.cors),
    5 ]
    1 from django.shortcuts import render
    2 
    3 
    4 def index(request):
    5     return render(request,"index.html")
     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <meta http-equiv="x-ua-compatible" content="IE=edge">
     6     <meta name="viewport" content="width=device-width, initial-scale=1">
     7     <title>Title</title>
     8     <script src="/static/jquery-3.2.1.js"></script>
     9 </head>
    10 <body>
    11 <h1>JIAのHOMESITE</h1>
    12 <input type="button" value="获取数据" onclick="getInfo()" />
    13 
    14 <script>
    15     var url = 'http://127.0.0.1:8000/get_data.html/';   // 服务端路径
    16     var $script;                                        // 模拟使用创建的标签名
    17 
    18     function getInfo() {
    19         $script = document.createElement('script');     // 创建一个 script 标签
    20         $script.setAttribute('src',url);                // 将需要请求数据的地址放入 script 的 src 中
    21         document.head.appendChild($script);             // 将标签放入到 head 中
    22     }
    23 
    24     function func(data) {                               // 数据返回后用来接收的函数
    25         console.log(data);
    26         document.head.removeChild($script);             // 接收完数据后,从页面删除刚才使用的标签
    27     }
    28 </script>
    29 </body>
    30 </html>

     

    通过JSONP自动完成 - 上面是它的原理


    服务端返回的数据

    1 from django.shortcuts import render,HttpResponse
    2 
    3 
    4 def get_data(request):
    5     # 根据后端发送过来的名字来决定返回时,数据外面套的内容
    6     funcName = request.GET.get('callback')
    7     return HttpResponse('%s("机密文件")'%funcName)

    客户端定义及获取数据

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <meta http-equiv="x-ua-compatible" content="IE=edge">
     6     <meta name="viewport" content="width=device-width, initial-scale=1">
     7     <title>Title</title>
     8     <script src="/static/jquery-3.2.1.js"></script>
     9 </head>
    10 <body>
    11 <h1>JIAのHOMESITE</h1>
    12 
    13 <script>
    14     function func(data) {
    15         console.log(data);
    16     }
    17 
    18     $(function () {
    19         var url = 'http://127.0.0.1:8000/get_data.html/';
    20         $.ajax({
    21             url:url,
    22             type:'GET',
    23             dataType:'JSONP',
    24             jsonp:'callback',
    25             jsonpCallback:'func'
    26         });
    27     })
    28 </script>
    29 </body>
    30 </html>

    二、CORS

    随着技术的发展,现在的浏览器可以支持主动设置从而允许跨域请求,即:跨域资源共享(CORS,Cross-Origin Resource Sharing),其本质是设置响应头,使得浏览器允许跨域请求。

    CORS将导致跨域访问的请求分为三种:Simple Request,Preflighted Request以及Requests with Credential。

    简单请求 和 非简单请求的区别

    简单请求只发送一次,直接发送数据;

    非简单请求则会发送两次数据,第一次发送 opption 请求(预检),为的是查看真正的数据是否可以被接收(数据头和请求方式是否符合要求), 如果符合要求,服务端可以把内容加入到头文件中来告诉浏览器;

    单请求 和 非简单请求的判断依据

    条件:
        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
         => 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过
            Access-Control-Request-Headers
    

     基于cors实现AJAX请求:

      a、支持跨域,简单请求

    在使用cors的时候,客户端几乎不用修改,只需要按照普通的ajax的请求方式发送;

    在服务端返回数据的时候,只要在返回的时候,加上一个响应头就可以解决这个问题了

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

    *的话,代表允许所有的请求

     服务端返回的数据

    1 from django.shortcuts import render,HttpResponse
    2 
    3 
    4 def get_data(request):
    5     if request.method == 'GET':
    6         response=HttpResponse('机密文件')
    7         response['Access-Control-Allow-Origin'] = '*'
    8         return response

     客户端定义及获取数据

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <meta http-equiv="x-ua-compatible" content="IE=edge">
     6     <meta name="viewport" content="width=device-width, initial-scale=1">
     7     <title>Title</title>
     8     <script src="/static/jquery-3.2.1.js"></script>
     9 </head>
    10 <body>
    11 <h1>JIAのHOMESITE</h1>
    12 
    13 <script>
    14 $(function () {
    15     var url = 'http://127.0.0.1:8000/get_data.html/';
    16     $.ajax({
    17         url:url,
    18         type:'GET',
    19         dataType:'text',
    20         success:function (data,statusText,xmlHttpRequest) {
    21             console.log(data);
    22         }
    23     })
    24 })
    25 
    26 </script>
    27 </body>
    28 </html>

      b、支持跨域,复杂请求

    对于复杂请求,会先发一次预检(OPTIONS)请求,如果服务端允许,那么再发送一次正式请求(如PUT等,总之就是真正的请求)。

    这个时候,我们需要在后端进行判断,如果允许用户获取数据,那么当预检(OPTIONS)过来的时候,我们需要返回允许访问的请求。

    • “预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method
    • “预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers
    • “预检”缓存时间,服务器设置响应头:Access-Control-Max-Age

     服务端返回的数据

     1 from django.shortcuts import render, HttpResponse
     2 
     3 
     4 def get_data(request):
     5 
     6 
     7     if request.method == 'OPTIONS':
     8         response = HttpResponse()                  // 返回的内容可以为空,主要需要返回请求头
     9         response['Access-Control-Allow-Origin'] = '*'
    10         response['Access-Control-Allow-Methods'] = 'PUT'  // 允许的复杂请求方式为 PUT 请求;
    11         response['Access-Control-Allow-Headers'] = 'k1'   // 复杂请求还有一种情况就是定制请求头,这种情况下,我们在返回的响应中应该设置该响应头,代表允许发送请求头的key是什么
     12 return response 13 14 if request.method == 'PUT': 15 response = HttpResponse('机密文件') 16 response['Access-Control-Allow-Origin'] = '*' 17 return response

     客户端定义及获取数据

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <meta http-equiv="x-ua-compatible" content="IE=edge">
     6     <meta name="viewport" content="width=device-width, initial-scale=1">
     7     <title>Title</title>
     8     <script src="/static/jquery-3.2.1.js"></script>
     9 </head>
    10 <body>
    11 <h1>JIAのHOMESITE</h1>
    12 
    13 <script>
    14 $(function () {
    15     var url = 'http://127.0.0.1:8000/get_data.html/';
    16     $.ajax({
    17         url:url,
    18         type:'PUT',
    19         dataType:'text',
    20         headers:{'k1':'v1'},
    21         success:function (data,statusText,xmlHttpRequest) {
    22             console.log(data);
    23 
    24             //获取响应头
    25             console.log(xmlHttpRequest.getAllResponseHeaders());  //Content-Type: text/html; charset=utf-8
    26         }
    27     })
    28 })
    29 
    30 </script>
    31 </body>
    32 </html>

     

    如果我们不想每次都经过“预检”这个环节的话,那么我们可以在服务器的响应头中增加一组:Access-Control-Max-Age的响应头,可以写成

    1 response['Access-Control-Allow-Headers'] = 10  // 默认单位为秒

      c、跨域获取响应头

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

      d、跨域传输cookie

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

    如果想要发送:

    • 浏览器端:XMLHttpRequest的withCredentials为true
    • 服务器端:Access-Control-Allow-Credentials为true
    • 注意:服务器端响应的 Access-Control-Allow-Origin 不能是通配符 *

     服务端返回的数据

     1 from django.shortcuts import render, HttpResponse
     2 
     3 
     4 def get_data(request): 9 
    10     if request.method == 'OPTIONS':
    11         response = HttpResponse()
    12         response['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8888'
    13         response['Access-Control-Allow-Methods'] = 'PUT'
    14         response['Access-Control-Allow-Headers'] = 'k1'
    15         response['Access-Control-Allow-Credentials'] = 'true'   #加在了这里
    16         return response
    17 
    18     if request.method == 'PUT':
    19         response = HttpResponse('机密文件')
    20         response['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8888'
    21         response['Access-Control-Allow-Credentials'] = 'true'     #加在了这里
    22         return response

     客户端定义及获取数据

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <meta http-equiv="x-ua-compatible" content="IE=edge">
     6     <meta name="viewport" content="width=device-width, initial-scale=1">
     7     <title>Title</title>
     8     <script src="/static/jquery-3.2.1.js"></script>
     9 </head>
    10 <body>
    11 <h1>JIAのHOMESITE</h1>
    12 
    13 <script>
    14 $(function () {
    15     var url = 'http://127.0.0.1:8000/get_data.html/';
    16     $.ajax({
    17         url:url,
    18         type:'PUT',
    19         dataType:'text',
    20         headers:{'k1':'v1'},
    21         xhrFields:{withCredentials:true},   //加在了这里
    22         success:function (data,statusText,xmlHttpRequest) {
    23             console.log(data);
    24 
    25             //获取响应头
    26             console.log(xmlHttpRequest.getAllResponseHeaders());
    27         }
    28     })
    29 })
    30 
    31 </script>
    32 </body>
    33 </html>

    参考博文:http://www.cnblogs.com/alwaysInMe/p/7686931.html

  • 相关阅读:
    什么是看板方法?
    瓶颈法则
    累积流图——你还没有用过吗?
    为什么我们关注看板方法?
    蒟蒻报道
    博客更换通知
    浅谈树套树(线段树套平衡树)&学习笔记
    浅谈FFT(快速博立叶变换)&学习笔记
    题解 洛谷P1903/BZOJ2120【[国家集训队]数颜色 / 维护队列】
    题解 洛谷P4550/BZOJ1426 【收集邮票】
  • 原文地址:https://www.cnblogs.com/metianzing/p/7868842.html
Copyright © 2020-2023  润新知