JSONP和CORS跨域

同源策略

  • 同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。所以xyz.com下的js脚本采用ajax读取abc.com里面的文件数据是会被拒绝的。
  • 同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。
  • 已经接收到了请求并返回了响应,是浏览器对非同源请求返回的结果做了拦截

一、源的定义

  • 如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源。
  • 下表给出了相对http://a.xyz.com/dir/page.html同源检测的示例:
URL结果原因
http://a.xyz.com/dir2/other.html 成功  
http://a.xyz.com/dir/inner/another.html 成功  
https://a.xyz.com/secure.html 失败 不同协议 ( https和http )
http://a.xyz.com:81/dir/etc.html 失败 不同端口 ( 81和80)
http://a.opq.com/dir/other.html 失败 不同域名 ( xyz和opq)

二、不受同源策略限制的

  • 页面中的链接,重定向以及表单提交是不会受到同源策略限制的。
  • 2. 跨域资源的引入是可以的。但是js不能读写加载的内容。如嵌入到页面中的<script src="..."></script>,<img>,<link>,<iframe>等。

三、JSONP解决跨域问题

  • JSONP的原型:创建一个回调函数,然后在远程服务上调用这个函数并且将JSON 数据形式作为参数传递,完成回调。
  • 将JSON数据填充进回调函数,这就是JSONP的JSON+Padding的含义。
复制代码
<!DOCTYPE HTML>
<html>
<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>xyz</title>
</head>
<body>
<button id="b1">点我</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
  function rion(res) {
    console.log(res);
  }

  function addScriptTag(src) {
    var scriptEle = document.createElement("script");
    $(scriptEle).attr("src", src);
    $("body").append(scriptEle);
    $(scriptEle).remove();
  }
  $("#b1").click(function () {
    addScriptTag("http://127.0.0.1:8002/abc/?callback=rion")
  });
</script>
</body>
</html>
html
复制代码
复制代码
def abc(request):
    res = {"code": 0, "data": ["SNIS-561", "SNIS-517", "SNIS-539"]}
    func = request.GET.get("callback")
    return HttpResponse("{}({})".format(func, json.dumps(res)))
view
复制代码
复制代码
<!DOCTYPE HTML>
<html>
<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>xyz</title>
</head>
<body>
<button id="b1">点我</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
  $("#b1").click(function () {
    $.getJSON("http://127.0.0.1:8002/abc/?callback=?", function (res) {
      console.log(res);
    })
  });

  //要注意的是在url的后面必须要有一个callback参数,这样getJSON方法才会知道是用JSONP方式去访问服务,
  // callback后面的那个?是jQuery内部自动生成的一个回调函数名。
</script>
</body>
</html>
jQuery中getJSON方法
复制代码
复制代码
<!DOCTYPE HTML>
<html>
<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>xyz</title>
</head>
<body>
<button id="b1">点我</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
  $("#b1").click(function () {
    $.ajax({
      url: "http://127.0.0.1:8002/abc/",
      dataType: "jsonp",
      jsonp: "callback",
      jsonpCallback: "rion2"  //如果服务端规定了回调函数名,可以使用$.ajax方法来实现:
    })
  });
  function rion2(res) {
    console.log(res);
  }


  //不过通常都会将回调函数写在success回调中:

   $("#b1").click(function () {
    $.ajax({
      url: "http://127.0.0.1:8002/abc/",
      dataType: "jsonp",
      success: function (res) {
        console.log(res);
      }
    })
  })

</script>
</body>
</html>
ajax
复制代码

四、CORS解决跨域问题

  • CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器发出XMLHttpRequest请求,从而解决AJAX只能同源使用的限制。
  • CORS需要浏览器和服务器同时支持。目前基本上主流的浏览器都支持CORS。所以只要后端服务支持CORS,就能够实现跨域。
  • 浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

1、简单请求

一个请求需要同时满足以下两大条件才属于简单请求。

复制代码
(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
复制代码

2、简单请求的处理方式

在跨域场景下,当浏览器发送简单请求时,浏览器会自动在请求头中添加表明请求来源的 Origin 字段。

后端程序只需要在返回的响应头中加上 Access-Control-Allow-Origin 字段,并且把该字段的值设置为 跨域请求的来源地址或简单的设置为 * 就可以了。

例如:可以在Django中间件中的process_response方法来给相应对象添加该字段。

复制代码
from django.utils.deprecation import MiddlewareMixin

class CorsMiddleware(MiddlewareMixin):

    def process_response(self, request, response):
        # 给响应头加上 Access-Control-Allow-Origin 字段 并简单的设置为 *
        response['Access-Control-Allow-Origin'] = '*'
        return response
process_response
复制代码

3、非简单请求的处理方式

  • 开发中常用到的那些请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json的都是非简单请求。
  • 对于非简单请求,浏览器通常都会在请求之前发送一次 OPTIONS 预检 请求。该请求会像后端服务询问是否允许从当前源发送请求并且询问允许的 请求方法 和 请求头字段。

解决办法:可以在后端简单的给响应对象添加上 常用请求方法(PUT、DELETE)的支持就可以了。

复制代码
from django.utils.deprecation import MiddlewareMixin


class CorsMiddleware(MiddlewareMixin):

    def process_response(self, request, response):
        # 给响应头加上 Access-Control-Allow-Origin 字段 并简单的设置为 *
        response['Access-Control-Allow-Origin'] = '*'
        if request.method == 'OPTIONS':
            # 允许发送 PUT 请求
            response['Access-Control-Allow-Methods'] = 'PUT, DELETE'
            # 允许在请求头中携带 Content-type字段,从而支持发送json数据
            response['Access-Control-Allow-Headers'] = 'Content-type'
        return response
process_response
复制代码

五、使用django-cors-headers跨域

上面中间件确实能解决目前的CORS跨域问题,但是不够严谨,已经有人造好轮子-- django-cors-headers 了。 更多详细配置详细请查看django-cors-headers项目

复制代码
1.安装
    pip install django-cors-headers
2.注册APP

    INSTALLED_APPS = [
        ...
        'app01.apps.App01Config',
        'corsheaders',  # 将 corsheaders 这个APP注册
    ]
3.添加中间件
    必须放在最前面,因为要先解决跨域的问题。只有允许跨域请求,后续的中间件才会正常执行。

    MIDDLEWARE = [
        'corsheaders.middleware.CorsMiddleware',  # 添加中间件
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        # 'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]

4.配置
    #可以选择不限制跨域访问
    CORS_ORIGIN_ALLOW_ALL = True

    # 或者可以选择设置允许访问的白名单
    CORS_ORIGIN_ALLOW_ALL = False
    CORS_ORIGIN_WHITELIST = (
        # '<YOUR_DOMAIN>[:PORT]',
        '127.0.0.1:8080'
    )
django-cors-headers 配置
复制代码

跨域请求

一 同源策略

同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现

请求的url地址,必须与浏览器上的url地址处于同域上,也就是域名,端口,协议相同.

比如:我在本地上的域名是127.0.0.1:8000,请求另外一个域名:127.0.0.1:8001一段数据

浏览器上就会报错,个就是同源策略的保护,如果浏览器对javascript没有同源策略的保护,那么一些重要的机密网站将会很危险

已拦截跨源请求:同源策略禁止读取位于 http://127.0.0.1:8001/SendAjax/ 的远程资源。(原因:CORS 头缺少 'Access-Control-Allow-Origin')。

但是注意,项目2中的访问已经发生了,说明是浏览器对非同源请求返回的结果做了拦截

二 CORS(跨域资源共享)简介

CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

三 CORS基本流程

浏览器将CORS请求分成两类:简单请求(simple request)非简单请求(not-so-simple request)。
浏览器发出CORS简单请求只需要在头信息之中增加一个Origin字段。
浏览器发出CORS非简单请求会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求(preflight)。浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

四 CORS两种请求详解

只要同时满足以下两大条件,就属于简单请求。

复制代码
(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
复制代码

凡是不同时满足上面两个条件,就属于非简单请求。

浏览器对这两种请求的处理,是不一样的。

复制代码
* 简单请求和非简单请求的区别?

   简单请求:一次请求
   非简单请求:两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用于数据传输。
* 关于“预检”

- 请求方式:OPTIONS
- “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息
- 如何“预检”
     => 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过
        Access-Control-Request-Method
     => 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过
        Access-Control-Request-Headers
复制代码

支持跨域,简单请求

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

支持跨域,复杂请求

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

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

五 Django项目中支持CORS

在返回的结果中加入允许信息(简单请求)

def test(request):
    import json
    obj=HttpResponse(json.dumps({'name':'lqz'}))
    # obj['Access-Control-Allow-Origin']='*'
    obj['Access-Control-Allow-Origin']='http://127.0.0.1:8004'
    return obj

放到中间件处理复杂和简单请求:

from django.utils.deprecation import MiddlewareMixin
class CorsMiddleWare(MiddlewareMixin):
    def process_response(self,request,response):
        if request.method=="OPTIONS":
            #可以加*
            response["Access-Control-Allow-Headers"]="Content-Type"
        response["Access-Control-Allow-Origin"] = "http://localhost:8080"
        return response

六 django 使用django-cors-headers 解决跨域问题

1、使用pip安装

pip install django-cors-headers

2、添加到setting的app中

INSTALLED_APPS = (
    ...
    'corsheaders',
    ...
)

3、添加中间件

MIDDLEWARE = [  # Or MIDDLEWARE_CLASSES on Django < 1.10
    ...
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    ...
]

4、setting下面添加下面的配置

复制代码
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',
)
复制代码

 

一 同源和跨域

  

  同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。    

  同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。当一个浏览器的两个tab页中分别打开来 百度和谷歌的页面当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。

  简单请求跨域

  我们创建两个django项目,第一个叫做s1,一个叫做s2,s1用8000端口启动,s2用8001端口启动

  s1项目的index.html文件内容如下:

复制代码
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h2>s1的首页</h2>
<button id="btn">Ajax请求</button>


<script src="https://cdn.bootcss.com/jquery/3.4.0/jquery.js"></script>
<script>
    $('#btn').click(function () {
        $.ajax({
            //url:'/books/', 访问自己服务器的路由,同源(ip地址、协议、端口都相同才是同源)
            url:'http://127.0.0.1:8001/books/', //访问其他服务器的路由,不同源,那么你可以访问到另外一个服务器,但是浏览器将响应内容给拦截了,并给你不同源的错误:Access to XMLHttpRequest at 'http://127.0.0.1:8001/books/' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'http://127.0.0.1:8002' that is not equal to the supplied origin.
            #并且注意ip地址和端口后面是一个斜杠,如果s2的这个url没有^books的^符号,那么可以写两个//。
       type:
'get', success:function (response) { console.log(response); } }) }) </script> </body> </html>
复制代码
复制代码

    urls.py文件内容如下:

复制代码
复制代码
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', views.index),
    url(r'^books/', views.books),
]
复制代码
复制代码

     views.py内容如下:

复制代码
复制代码
from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
# Create your views here.

def index(request):
    return render(request,'index.html')

def books(request):

    # return JsonResponse(['西游记','三国演义','水浒传'],safe=False)
    obj = JsonResponse(['西游记','三国演义','水浒传'],safe=False)
    return obj
复制代码
复制代码

 

   s2项目的urls.py内容如下:

复制代码
复制代码
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^books/', views.books),
]
复制代码
复制代码

     views.py内容如下:

复制代码
复制代码
from django.shortcuts import render
from django.http import JsonResponse
# Create your views here.
def books(request):

    # return JsonResponse(['西游记2','三国演义2','水浒传2'],safe=False)

    obj = JsonResponse(['西游记2','三国演义2','水浒传2'],safe=False)
    #下面这个响应头信息是告诉浏览器,不要拦着,我就给它,"*"的意思是谁来请求我,我都给
    # obj["Access-Control-Allow-Origin"] = "*"
    obj["Access-Control-Allow-Origin"] = "http://127.0.0.1:8000" #只有这个ip和端口来的请求,我才给他数据,其他你浏览器帮我拦着
    return obj
复制代码
复制代码

 

   以上是一个简单请求的跨域问题和解决方法。 

 

 

二 CORS通信实现跨域

  

  CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。

  整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

  因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

  

  浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

  只要同时满足以下两大条件,就属于简单请求

复制代码
复制代码
(1) 请求方法是以下三种方法之一:(也就是说如果你的请求方法是什么put、delete等肯定是非简单请求)
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,也就是说,如果你发送的application/json格式的数据,那么肯定是非简单请求,vue的axios默认的请求体信息格式是json的,ajax默认是urlencoded的。
复制代码
复制代码

  凡是不同时满足上面两个条件,就属于非简单请求

  我们改一下上一节的s1项目的index.html文件中的ajax里面的内容:

复制代码
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h2>s1的首页</h2>
<button id="btn">Ajax请求</button>


<script src="https://cdn.bootcss.com/jquery/3.4.0/jquery.js"></script>
<script>
    $('#btn').click(function () {
        $.ajax({
            url:'http://127.0.0.1:8001/books/',
            type:'post',
            contentType:'application/json',//非简单请求,会报错:Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.
            data:JSON.stringify({
               a:'1'
            }),
//headers:{b:2}, success:
function (response) { console.log(response); } }) }) </script> </body> </html>
复制代码
复制代码

   

 

  浏览器对这两种请求的处理,是不一样的。

复制代码
复制代码
* 简单请求和非简单请求的区别?

   简单请求:一次请求
   非简单请求:两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用于数据传输。
* 关于“预检”

- 请求方式:OPTIONS
- “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息
- 如何“预检”
     => 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过
        Access-Control-Request-Method
     => 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过
        Access-Control-Request-Headers
复制代码
复制代码

    看图:

     

     

  s2项目的views.py内容如下:

复制代码
复制代码
from django.shortcuts import render
from django.http import JsonResponse
# Create your views here.
def books(request):

    # return JsonResponse(['西游记2','三国演义2','水浒传2'],safe=False)

    obj = JsonResponse(['西游记2','三国演义2','水浒传2'],safe=False)
    # obj["Access-Control-Allow-Origin"] = "*"
    obj["Access-Control-Allow-Origin"] = "http://127.0.0.1:8000"
    print(request.method)
    #处理预检的options请求,这个预检的响应,我们需要在响应头里面加上下面的内容
    if request.method == 'OPTIONS':
        # obj['Access-Control-Allow-Headers'] = "Content-Type" #"Content-Type",首字母小写也行
        # obj['Access-Control-Allow-Headers'] = "content-type" #"Content-Type",首字母小写也行。这个content-type的意思是,什么样的请求体类型数据都可以,我们前面说了content-type等于application/json时,是复杂请求,复杂请求先进行预检,预检的响应中我们加上这个,就是告诉浏览器,不要拦截
        obj['Access-Control-Allow-Headers'] = "content-type,b"  #发送来的请求里面的请求头里面的内容可以定义多个,后端需要将头配置上才能访问
    return obj
复制代码
复制代码

 

 

  支持跨域,简单请求

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

  支持跨域,复杂请求

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

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

      “预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers

  

  关于请求方式的跨域问题及解决方法:

  s1的index.html文件内容如下:

复制代码
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h2>s1的首页</h2>
<button id="btn">Ajax请求</button>


<script src="https://cdn.bootcss.com/jquery/3.4.0/jquery.js"></script>
<script>
    $('#btn').click(function () {
        $.ajax({
            url:'http://127.0.0.1:8001/books/',
            //type:'delete',
            //type:'post',
            type:'put',
            contentType:'application/json',//非简单请求,会报错:Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.
            data:JSON.stringify({
               a:'1'
            }),
            headers:{b:'2'},
            success:function (response) {
                console.log(response);
            }

        })
    })


</script>
</body>
</html>
复制代码
复制代码

  s2项目的views.py内容如下:

复制代码
复制代码
from django.shortcuts import render
from django.http import JsonResponse
# Create your views here.
def books(request):

    # return JsonResponse(['西游记2','三国演义2','水浒传2'],safe=False)

    obj = JsonResponse(['西游记2','三国演义2','水浒传2'],safe=False)
    # obj["Access-Control-Allow-Origin"] = "*"
    obj["Access-Control-Allow-Origin"] = "http://127.0.0.1:8000"
    print(request.method)
    #处理预检的options请求,这个预检的响应,我们需要在响应头里面加上下面的内容
    if request.method == 'OPTIONS':
       
        obj['Access-Control-Allow-Headers'] = "content-type,b"  #发送来的请求里面的请求头里面的内容可以定义多个,后端需要将头配置上才能访问
        obj['Access-Control-Allow-Methods'] = "DELETE,PUT"  #通过预检的请求方法设置

    return obj
复制代码
复制代码

 

 

三 Jsonp实现跨域

  Jsonp

    jsonp是json用来跨域的一个东西。原理是通过script标签的跨域特性来绕过同源策略。

    思考:这算怎么回事?

<script src="http://code.jquery.com/jquery-latest.js"></script>

 

    script标签的src属性是可以直接实现跨域的,这是标签特性,所以我们可以借助它来实现跨域。

复制代码
复制代码
# =============================http://127.0.0.1:8001/index.html


<button>ajax</button>
{% csrf_token %}

<script>
    function func(name){ //func写在下面src请求的上面,不然找不到这个func函数,我们请求的另一个网站的后端返回的数据就是这个函数名称加参数的形式  // #HttpResponse("func('chao')")
        alert(name)
    }
</script>

<script src="http://127.0.0.1:8002/SendAjax/"></script>


# =============================http://127.0.0.1:8002/
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt  #如果跨域请求是post请求,别忘了csrftoken可能会产生的阻拦,get请求不需要加上它


def SendAjax(request):

    import json

    print("++++++++")
    # dic={"k1":"v1"}
    return HttpResponse("func('chao')")  # return HttpResponse("func('%s')"%json.dumps(dic))
# 注意,func这个名称是另外那个发送请求的网站的一个js代码函数名称
复制代码
复制代码

  

  这其实就是JSONP的简单实现模式,或者说是JSONP的原型:创建一个回调函数,然后在远程服务上调用这个函数并且将JSON 数据形式作为参数传递,完成回调。

  将JSON数据填充进回调函数,这就是JSONP的JSON+Padding的含义。

  一般情况下,我们希望这个script标签能够动态的调用,而不是像上面因为固定在html里面所以没等页面显示就执行了,很不灵活。我们可以通过javascript动态的创建script标签,这样我们就可以灵活调用远程服务了。

复制代码
复制代码
<button onclick="f()">sendAjax</button>

<script>
    function addScriptTag(src){
         var script = document.createElement('script');
         script.setAttribute("type","text/javascript");
         script.src = src;
         document.body.appendChild(script);
         document.body.removeChild(script);
    }


    function func(name){  #接受返回数据的回调函数
        alert("hello"+name)
    }

    function f(){
         addScriptTag("http://127.0.0.1:8002/SendAjax/")
    }
</script>
复制代码
复制代码

 

   

    为了更加灵活,现在将你自己在客户端定义的回调函数的函数名传送给服务端,服务端则会返回以你定义的回调函数名的方法,将获取的json数据传入这个方法完成回调,这样的话,后端服务器就不需要知道你的回调函数的名称是什么了,不然和上面似的,服务端必须知道你的函数名称,就不太灵活:

    将8001的f()改写为:

function f(){
         addScriptTag("http://127.0.0.1:8002/SendAjax/?callbacks=func") // callbacks这个名称不是固定的,和另一个网站的服务端商量好就行了,就是后端取值时的一个参数而已
    }

     8002的views改为:

复制代码
复制代码
def SendAjax(request):
 
    import json
 
    dic={"k1":"v1"}
 
    print("callbacks:",request.GET.get("callbacks"))
    callbacks=request.GET.get("callbacks")
 
    return HttpResponse("%s('%s')"%(callbacks,json.dumps(dic)))
复制代码
复制代码

 

  jQuery对JSONP的实现

    getJSON

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

      8001的html改为:

复制代码
复制代码
<button onclick="f()">sendAjax</button>

<script>

    function f(){
          $.getJSON("http://127.0.0.1:8002/SendAjax/?callbacks=?",function(arg){
            alert("hello"+arg)
        });
    }
    
</script>
复制代码
复制代码

      8002的views不改动。

      结果是一样的,要注意的是在url的后面必须添加一个callbacks(callbacks名不是固定的)参数,这样getJSON方法才会知道是用JSONP方式去访问服务,callbacks后面的那个问号是内部自动生成的一个回调函数名。

          此外,如果说我们想指定自己的回调函数名,或者说服务上规定了固定回调函数名该怎么办呢?我们可以使用$.ajax方法来实现

    $.ajax

      8001的html改为:

复制代码
复制代码
<script>

    function f(){
          $.ajax({  //大家会发现我的type请求方法没写,默认是get请求
                url:"http://127.0.0.1:8002/SendAjax/",
                dataType:"jsonp",
                jsonp: 'callbacks',
                jsonpCallback:"SayHi"
           });

       }

    function SayHi(arg){
                alert(arg.k1);  //k1是后端返回的数据中的键值对的键
            }

</script>
复制代码
复制代码

      8002的views不改动。

      当然,最简单的形式还是通过回调函数来处理:

复制代码
复制代码
<script>

    function f(){

            $.ajax({
               url:"http://127.0.0.1:8002/SendAjax/",
               dataType:"jsonp",            //必须有,告诉server,这次访问要的是一个jsonp的结果。
               jsonp: 'callbacks',          //jQuery帮助随机生成的:callbacks="wner"
               success:function(data){
                   alert("hi "+data)
              }
         });

       }

</script>
复制代码
复制代码

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

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

      注意 JSONP一定是GET请求

 

  应用:

复制代码
复制代码
<input type="button" onclick="AjaxRequest()" value="跨域Ajax" />


<div id="container"></div>


    <script type="text/javascript">
        function AjaxRequest() {
            $.ajax({
                url: 'http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list&_=1454376870403',
                type: 'GET',
                dataType: 'jsonp',
                jsonp: 'callback',
                jsonpCallback: 'list',
                success: function (data) {
                    
                    $.each(data.data,function(i){
                        var item = data.data[i];
                        var str = "<p>"+ item.week +"</p>";
                        $('#container').append(str);
                        $.each(item.list,function(j){
                            var temp = "<a href='" + item.list[j].link +"'>" + item.list[j].name +" </a><br/>";
                            $('#container').append(temp);
                        });
                        $('#container').append("<hr/>");
                    })

                }
            });
        }
</script>
复制代码
复制代码

 

posted @   silencio。  阅读(102)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示