zoukankan      html  css  js  c++  java
  • 跨域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

  • 相关阅读:
    mysql BETWEEN操作符 语法
    mysql IN操作符 语法
    mysql LIKE通配符 语法
    mysql TOP语句 语法
    mysql DELETE语句 语法
    mysql Update语句 语法
    mysql INSERT语句 语法
    mysql ORDER BY语句 语法
    mysql OR运算符 语法
    mysql AND运算符 语法
  • 原文地址:https://www.cnblogs.com/metianzing/p/7868842.html
Copyright © 2011-2022 走看看