AJAX异步请求

准备知识

在学习AJAX前,咱们先了解一下JSON和XMLjavascript

JSON

定义

JSON(JavaScript Object Notation, JS对象标记),是一种轻量级的数据交换格式。css

它基于 ECMAScript (w3c制定的JS规范)的一个子集,采用彻底独立于编程语言的文本格式来存储和表示数据。
简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提高网络传输效率。html

JSON对象和JSON字符串

咱们知道python中有一个json模块,经过json.dumps()方法,咱们能够将python中的基本数据类型序列化为一种标准格式的字符串,进而能够存储或经过网络传输;经过json.loads()方法,又能够将这些标准格式的字符串反序列化为原数据类型。这种标准格式的字符串就是JSON字符串,前端

JSON对象是JS对象的子集,它包含JS中的6种数据类型:number, string, Boolean, array, null, object。也就是JSON对象必定是JS对象。注意,JS能够接受单引号,双引号的string类型,可是JSON中只有双引号的string。java

JSON字符串的格式很简单,将JSON对象加一对单引号' '包起来就是JSON字符串,好比:JS数字1 转为JSON字符串是’1’ ,JS对象{"name": "Seb"}转换为JSON字符串是’{“name”: “Seb”}` 。注意,对于JS中的单引号string,它会将其转换为双引号string,而后再用单引号包起来。好比:JS字符串'cat'"cat"转换为JSON字符串都是'"cat"'python

在JS中,经过JSON.stringify()方法,能够将JSON对象转化为JSON字符串;经过JSON.parse()方法,能够将JSON字符串转化为JSON对象。jquery

XML

XML(extensible markup language, 可扩展标记语言)也是一种数据交换的格式,它比JSON出现的更早,它的格式相似于HTML的标签。git

图片1

对比JSON:
这里写图片描述github

对比可知,一样的信息,JSON所用的字符要比XML少不少,于是在网络传输方面更具优点。目前,JSON已经成为各大网站交换数据的标准格式。web

AJAX

AJAX(Asynchronous JavaScript And XML),异步JS和XML,即便用JS语言与服务器进行异步交互,传输数据格式为XML(不过目前JSON格式已经在大部分领域取代了XML)。AJAX除了支持异步交互,另外一个特色就是浏览器页面的局部刷新,因为不须要重载整个页面,不只提升了性能,还提高了用户体验。

好比,咱们日常网站登陆或则注册,对于咱们输入内容的验证,就是基于AJAX技术,给服务器发送用户输入的数据,服务器将验证的结果用JSON格式的字符串发回响应,前端用JS来解析JSON数据,若是有错误信息,就经过JS在页面添加错误提示;若是验证经过,就跳转至首页。

这里写图片描述

AJAX是基于JS的一门技术,不过JS的语法比较繁琐,并且还要处理不一样浏览器的兼容问题,所以,通常咱们经过JQuery使用AJAX,JQuery语法简洁,并且解决了浏览器兼容问题。想了解更多JQuery知识,能够参考个人另外一篇博文

Ajax的jQuery实现:

下面咱们看一下,经过jQuery发送ajax请求的基本形式:
前端:

<form action="{% url 'login' %}" method="post">
    <div class="form-group">
        <label for="username">用户名</label>
        <input type="text" class="form-control" id="username" name="username" required>
    </div>
    <div class="form-group">
        <label for="password">密码</label>
        <input type="password" class="form-control" id="password" name="password" required>
    </div>
    <p>
    <button type="submit" class="btn btn-primary btn-block">登陆</button>
    </p>
</form>

<script> // 点击提交按钮,ajax发送;验证成功,经过location.href = url来跳转 $('form').submit(function(e){ e.preventDefault(); //阻止默认提交 var username = $("[name='username']").val(); // 注意,jquery筛选必定加引号,不然报错uncaught var password = $("[name='password']").val(); $.ajax({ url: "{% url 'login' %}", type: "POST", data: { "username": username, "password": password, }, success: function(res) { //res是server端响应 response = JSON.parse(res); //将json字符串解析为json对象(即JS对象) if (response['errors']) { console.log(response['errors']); } else { location.href = "/index/"; //跳转至首页 } </script>

后端:

def login(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password') 
        //若是前端发来的data中key对应的value是列表,要用getlist(key)来获取
        ajax_response = {'user': None, "errors": ""}
        if username == 'egon' and password == '123':
            ajax_response['user']='egon'
        else:
            ajax_response['errors']='用户名或密码错误'
        return HttpResponse(json.dumps(ajax_response))
    if request.method == 'GET':
        return render(request, 'login.html')

参数说明

经过jQuery发送Ajax请求的几个基本参数:

  1. url, 请求的地址
  2. type, 请求的方法:默认是get
  3. data, 请求要携带的数据,是一个json的object对象,相似python中的字典
  4. 基本流程:1. Ajax发送请求 2. server接收响应 3. server处理数据 4. server返回响应 5. Ajax接收响应;若是以上都顺利进行(server返回200 ok 状态码),就会执行success参数对应的函数。可选的还有error, server端错误时执行的函数; complete, 不管是否错误,都执行complete对应的函数; statusCode, 根据状态码执行不一样的函数,好比:statusCode: {'403': function(){}, '401': function(){}}


其它参数:

  1. processData, 声明当前的data数据是否进行转码或预处理,默认为true,即预处理。

  2. contentType, 发送信息至服务器时内容编码类型,默认值: application/x-www-form-urlencodedURL编码。注意,在Django中,其request.POST是从请求体request.body中转化过来的,它只识别URL编码的内容,即name=xxx&age=xxx这种格式的内容。所以,若是在后端经过Django获取数据时,request.POST可能为空,这时能够从request.body中去找数据,其中必定有值,除非对方没有发送。

  3. traditional, 通常是咱们的data数据有数组时会用到,不过对于数组和字典,更好的处理方式是JSON.stringfy()序列化后再发送:

    $.ajax({
        url: '/test_ajax.html',
        type: 'POST',
        data: {
            'name': 'Ayhan',
            'age': 18,
            'hobby': ['eating', 'sleeping', 'playing'], //数组
        },
        traditional: true, //有它,才能正确发送数组
        succes: function(response) {
            console.log(response);
        }
    })

    说明:

    1. 若是不设置traditional: true,后台拿到的数据是这样的:b'name=Ayhan&age=18&hobby%5B%5D=eating&hobby%5B%5D=sleeping&hobby%5B%5D=playing', %5B%5D表示中括号[],这是由于Ajax发送数据默认带请求头带内容编码"Content-type", "application/x-www-form-urlencoded",表示客户端提交给服务器文本内容的编码方式是URL编码,即除了标准字符外,每字节以双字节16进制前加个“%”表示。
    2. 设置了traditional: true后,后台拿到的数据是这样的:b'name=Ayhan&age=18&hobby=eating&hobby=sleeping&hobby=playing',会对数组进行深层迭代,不过仍是须要手动分割信息。
  4. dataType: 'JSON',若是给出这个参数,指定返回的响应必须是json格式;这样服务端返回的json响应不须要JSON.parse解析就能够直接做为json对象使用:

    $.ajax({
            url: '/test_ajax.html',
            type: 'GET',
            data: {},
            dataType: 'JSON', //指定返回的响应必须是JSON字符串格式
            success: function (response) {
                console.log(response.stauts);
                console.log(response.msg);
            }
        })

    后端逻辑:

    from django.http import JsonResponse
    
    def test_ajax(request):
    
        response = {
                'status': 1,
                'msg': 'hello, handsome!',
            }
    
        return JsonResponse(response)

    说明:

    return JsonResponse(response)至关于return HttpResponse(json.dumps(response))

    JsonResponse若是接收列表,会报错,它默认列表属于不规范的数据,没有key,不能包含状态等详细信息。

经过JSON发送数组/列表,字典

Ajax的data中,若是数据是字符串或数字,均可以直接发送。可是对于数组/列表,字典,最好的方式是将数进行JSON.stringfy()序列化发给后端:

var res = {'name': 'Ayhan', 'age': 18, 'hobby': ['eating', 'sleeping', 'playing']};

$.ajax({
    url: '/test_ajax.html/',
    type: 'POST',
    data: JSON.stringify(res), //数据总体序列化
    success: function (response) {
        console.log(response);
    }
})

这样,在Django后台就能够经过request.body中拿到b'{"name":"Ayhan","age":18,"hobby":["eating","sleeping","playing"]}'这样的信息,直接反序列化json.loads(request.body.decode('utf-8')),就能够拿到字典对象。


固然也能够单独序列化数组,后端经过key提取字符串再反序列化为列表json.loads(request.POST.get('hobby'))便可:

$.ajax({
    url: '/test_ajax.html/',
    type: 'POST',
    data: {
        'name': 'Ayhan',
        'age': 18,
        'hobby': JSON.stringify(['eating', 'sleeping', 'playing']) //单独序列化数组
    },
    success: function (response) {
        console.log(response);
    }
})

说明:这种方式对于处理data中包含字典格式的数据也是适用的。

Ajax前置操做及全局设置

beforeSend

在发送Ajax以前,咱们能够利用beforeSend参数做一些前置操做,好比为请求设置csrf-token,若是是在模板中,那么直接多发送送一个键值对便可,引擎会渲染出来,参考博客。可是若是是在本地客户端,对于POST, DELETE, PUT等请求方式,会被CSRF拦截,怎么作呢?

经过cookie,在请求头中设置csrf-token键值对。注意,jQuery原生不支持cookie操做,须要导入扩展jquery.cookie.js

下面咱们看一下如何操做:

  1. 第一次GET请求服务端时,服务端返回cookie信息

    def server(request):
        from django.middleware.csrf import get_token
        get_token(request)  # Returns the CSRF token
        return render(request, 'server.html')
  2. 之后客户端访问时,在请求头中带着这个cookie信息就能够了(前提须要引入扩展jquery.cookie.js)。

    //方式一:直接设置请求头headers
    $.ajax({
        url: requestUrl,
        type: 'delete',
        data: JSON.stringify(ids),
        dataType: 'JSON',
        headers: {'X-CSRFToken': $.cookie('csrftoken')}, 
    })
    
    //方式二:beforSend参数,在发送请求前,执行一些操做
    $.ajax({
        url: requestUrl,
        type: 'delete',
        data: JSON.stringify(ids),
        dataType: 'JSON',
        beforeSend: function (xhr) {
            // 请求头中设置一次csrf-token
            xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
        },
    })

    说明:不管是哪一种方式,本质都是经过setRequestHeader方法为 XMLHttpRequest对象设置请求头。

ajaxSetup

虽然经过如上方式,能够解决CSRF问题,可是,每次都要手动设置。其实咱们能够利用ajaxSetup来做全局设置,每次发送Ajax前,执行一些特定操做:

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection 
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); -- 正则匹配
}

$.ajaxSetup({
    beforeSend: function (xhr, settings) {
        // 全局Ajax中添加请求头X-CSRFToken,用于跨过CSRF验证
        if (!csrfSafeMethod(settings.type)) {
            xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
        }
    }
});

这样,只需将这段代码放到发送Ajax请求前,就能够根据请求方式自动设置CSRFToken的cookie信息。

Ajax跨域

Ajax没法处理跨域请求

当一个资源从与该资源自己所在的服务器不一样的域或端口请求一个资源时,资源会发起一个跨域 HTTP 请求

出于安全缘由,全部浏览器都遵循同源策略(same-origin policy,具体请自行搜索),它限制Ajax处理跨域请求:当Ajax中url参数是外部域名时,尽管Ajax能够将请求发送出去,可是服务端返回的响应会被浏览器阻止。下面咱们来模拟下这种状况:

  1. 在Django中新建两个web项目:

    项目A提供API(URL):http://127.0.0.1:9000/get_data.html,它返回一些字符串:

    from django.shortcuts import HttpResponse
    
    def get_data(request):
        return HttpResponse('来自火星的遥远电波')


    项目B中的域名是:http://127.0.0.1:8000/index.html/,访问它将返回欢迎页面,并在页面加载完后,对API发起跨域Ajax请求:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="/static/plugins/jquery-3.2.1.js"></script>
        <script> $(function () { var API = 'http://127.0.0.1:9000/get_data.html'; $.ajax({ url: API, type: 'GET', success: function (response) { console.log(response) } }) }) </script>
    </head>
    <body>
    <h1>欢迎访问主页</h1>
    <hr>
    </body>
    </html>


  2. 在chrome浏览器中访问项目B的域名:http://127.0.0.1:8000/index.html/,右键查看Console(控制台)信息:

    1.png


  3. 若是查看项目A运行的状态信息,确实收到了一次GET请求,而且响应200状态码,说明请求被成功处理并返回了响应:

    2.png


所以,在浏览器中,Ajax能够将跨域请求发送出去,而且服务端也处理了请求,只是响应被浏览器阻止了。那么若是解决呢?

解决方案

下面给出三种解决方案:requests经过后台服务器发送,JSONP与COSRS仍是经过浏览器。

requests

经过后台requests模块发送跨域请求,Ajax再从后台请求数据数据,这样Ajax仍是从同源地址获取数据,略。

JSONP

回顾标签的src属性和<script>标签

仍是上面的栗子,若是咱们在项目A页面中提供一个API:"http://127.0.0.1:9000/static/img/ayhan_huang.jpg",在页面中经过<img>标签的src属性来请求这个API:

<img src="http://127.0.0.1:9000/static/img/ayhan_huang.jpg">

这时,网页能够正常显示这张图片:

3.png


相似的还有<script>, 标签,好比咱们经过CDN引入网络上的CSS或JS文件:<script src="https://cdn.bootcss.com/jquery/3.2.1/core.js"></script>

<iframe>标签,请求另外一个网站的内容并嵌套在当前窗口:<iframe src="http://blog.csdn.net/Ayhan_huang/article/details/78220032" style="width: 200px; height: 300px"></iframe>

这些标签都具备src属性,总结:src属性的标签通常不受同源策略的限制。


基于以上分析,下面咱们不经过Ajax,而是直接用<script>标签对API发起访问:

<script src="http://127.0.0.1:9000/get_data.html"></script>

查看chrome Console信息:

4.png

错误提示:’来自火星的遥远电波’未定义

说明,咱们经过<script>标签引入的内容,会做为JS代码执行,这里是收到服务器A返回的字符串后,将其视做JS代码来执行,所以发生未定义错误。知道这一点后,只要想办法让服务器A返回的字符串符合JS代码的语法,就能够顺利执行!好比,若是服务器A返回的响应是"alert('来自火星的遥远电波')",那么页面中收到该字符串响应后,执行并弹出窗口:

5.png


函数整合

为了拿到并操做跨域的数据,咱们根据以上思路用回调函数整合一下:

  1. 本地客户端:

    <script>
    
        function func(ars) {
            console.log(arg);
        }
    
    </script> <script src="http://127.0.0.1:9000/get_data.html"></script>
  2. 远程服务端:

    def get_data(request):
        return HttpResponse("func('来自火星的遥远电波')")

    说明:客户端定义函数func,服务端发送的数据用"func()"包起来,这样客户端经过<script>src属性获取到服务端的响应后,就会将响应数据做为参数,执行func函数。


经过这种方式,须要约定远程服务端返回的响应字符串具备某种格式,好比上面的"func('XXX')",以便做为JS代码在本地执行。所以能够,1. 咱们能够将外层包的函数名做为URL的查询字符串发送过去,服务端从请求中提取函数名,并用函数名包裹数据,返回响应。2. 另外,咱们还但愿<script>标签是实时生成的,用完即删除。基于这两点,咱们能够将上面的代码从新改造:

  1. 本地客户端:

    <!DOCTYPE html>
    <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="/static/plugins/jquery-3.2.1.js"></script> </head> <body> <h1> 欢迎访问主页 </h1> <hr> <button id="send-jsonp">点我发送JSONP</button> <script> //回调函数,执行完删除script标签 function func(arg) { console.log(arg); document.head.removeChild(tag); } //建立script标签并赋值src属性 function jsonp(url) { tag = document.createElement('script'); tag.src = url; document.head.appendChild(tag); console.log(tag) } //回调函数做为url查询字符串传递:?callback=func var API = 'http://127.0.0.1:9000/get_data.html?callback=func'; var btn = document.getElementById('send-jsonp'); btn.onclick = function () { jsonp(API); }; </script> </body> </html>
  2. 远程服务端:

    from django.shortcuts import HttpResponse
    
    def get_data(request):
        func_name = request.GET.get('callback')
        print(func_name)
        return HttpResponse("{func_name}('来自火星的遥远电波')".format(func_name=func_name))

JSONP

JSONP正是利用了上述原理:本质就是生成<script>标签,经过src发起GET请求,而不是经过Ajax发送请求。从效果上来讲,它成功拿到了数据,页面也没有刷新,而且不受同源策略的限制。经过jQuery,咱们能够不用手动实现上面的过程,更方便的经过JSONP发送请求。

<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="/static/plugins/jquery-3.2.1.js"></script> </head> <body> <h1> 欢迎访问主页 </h1> <hr> <button id="send-jsonp">点我发送JSONP</button> <script> var btn = document.getElementById('send-jsonp'); btn.onclick = function () { $.ajax({ url: 'http://127.0.0.1:9000/get_data.html', type: 'GET', dataType: 'JSONP', success: function (response) { console.log(response); } }) }; /* //也能够单独定义回调函数,经过jsonpCallback参数指定;该函数名最终会拼接到url查询字符串后面 function func(response) { console.log(response); } btn.onclick = function () { $.ajax({ url: 'http://127.0.0.1:9000/get_data.html', type: 'GET', dataType: 'JSONP', jsonpCallback: 'func', }) }; */ </script> </body> </html>


远程服务器逻辑不变:

from django.shortcuts import HttpResponse

def get_data(request):
    func_name = request.GET.get('callback')
    print(func_name)
    return HttpResponse("{func_name}('来自火星的遥远电波')".format(func_name=func_name))

说明:

  1. JSONP的本质决定了只支持GET请求,即便经过jQuery改成POST方式,仍是被转为GET请求。

  2. 虽然形式是经过Ajax发送,可是内部并非Ajax。

  3. success: function () {}内部会将回调函数function添加到url后面的查询字符串(不是明文添加,远程打印出来是回掉函数名是jQuery321019246026086630175_1508238544570这样的形式,每次都会变)。

  4. 远程仍是必须经过查询字符串获取函数名request.GET.get('callback'),将数据包起来。


实例

找了一个江西卫视的节目单的API:http://www.jxntv.cn/data/jmd-jxtv2.html,浏览器访问结果以下:

7.png

尽管内容乱码,可是发现数据结构能够看做是list()函数包裹的字符串,所以,咱们利用JSONP,定义回调函数list(),来请求这个API:

<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="/static/plugins/jquery-3.2.1.js"></script> </head> <body> <h1> 欢迎访问主页 </h1> <hr> </div> <script> function list(response) { console.log(response); } var url = 'http://www.jxntv.cn/data/jmd-jxtv2.html'; $.ajax({ url: url, type: 'GET', dataType: 'JSONP', jsonpCallback: 'list', }) </script> </body> </html>

查看打印结果:

7.png

全部数据已经所有获取到了。

CORS

如今的浏览器能够支持主动设置从而容许跨域请求,即Cross-Origin Resource Sharing 跨域资源共享,其本质是设置响应头,使得浏览器容许跨域请求。它和JSONP同样,也是经过浏览器发送请求,不一样的是JSONP绕过了同源策略,而CORS则是主动解决问题。以前咱们经过Ajax发送跨域http请求时,浏览器给出的错误信息是:

Failed to load http://127.0.0.1:9020/get_data.html: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:8000' is therefore not allowed access.

其实就是远程服务器响应头中缺乏信息'Access-Control-Allow-Origin'及其值'http://127.0.0.1:8000'。所以经过CORS解决同源策略,本地客户端不用作任何操做,只需修改远程服务器的后台逻辑,添加以上请求头信息便可。

仍是上面的例子,如今客户端能够直接经过Ajax发送请求:

<!DOCTYPE html>
<html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="/static/plugins/jquery-3.2.1.js"></script> </head> <body> <h1> 欢迎访问主页 </h1> <hr> <button id="magic-btn">Send CORS</button> </div> <script> var API = 'http://127.0.0.1:9020/get_data.html'; $('#magic-btn').click(function () { $.ajax({ url: API, type: 'GET', success: function (response) { console.log(response); } }) )} </script> </body> </html>


远程服务端修改,设置响应头:

def get_data(request):
    response = HttpResponse('来自火星的遥远电波')
    response['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8000'
    return response


跨域的其它知识

  1. 简单请求和非简单请求:

    • 简单请求:一次请求
    • 非简单请求:两次请求,在发送数据以前会先发一次请求用于作“预检”,只有“预检”经过后才再发送一次请求用于数据传输。
  2. 如何判断:

    条件:
        一、请求方式:HEAD、GET、POST
        二、请求头信息:
            Accept
            Accept-Language
            Content-Language
            Last-Event-ID
            Content-Type 对应的值是如下三个中的任意一个
                                    application/x-www-form-urlencoded
                                    multipart/form-data
                                    text/plain

    注意:同时知足以上两个条件时,则是简单请求,不然为复杂请求

  3. 预检:

    - 请求方式:OPTIONS
    - “预检”其实作检查,检查若是经过则容许传输数据,检查不经过则再也不发送真正想要发送的消息
    - 如何“预检”
         => 若是复杂请求是PUT等请求,则服务端须要设置容许某请求,不然“预检”不经过
            Access-Control-Request-Method
         => 若是复杂请求设置了请求头,则服务端须要设置容许某请求头,不然“预检”不经过
            Access-Control-Request-Headers
    def get_data(request):
        if request.method == 'OPTIONS': # 预检
            response = HttpResponse() # 预检通常不须要返回数据
            response['Access-Control-Allow-Origin'] = '*' # 容许的域名,* 是全部
            response['Access-Control-Allow-Methods'] = 'PUT' # 设置容许的请求方式
            response['Access-Control-Allow-Headers'] = 'key' # 容许的请求头
            return response
        else:
            response = HttpResponse('来自火星的遥远电波')
            return response
  4. 跨域cookie

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

    若是想要发送:

    • 浏览器端:XMLHttpRequest的withCredentials为true
    • 服务器端:Access-Control-Allow-Credentials为true
    • 注意:服务器端响应的 Access-Control-Allow-Origin 不能是通配符 *
    $.ajax({
        url: API,
        type: 'PUT',
        dataType: 'text',
        headers: {'k1': 'v1'}, //设置了自定义请求头,便是复杂请求。
        xhrFields: {withCredentials: true},
        success: function (data) {
            console.log(data);
        }
    });
    response['Access-Control-Allow-Credentials'] = 'true'
  5. 补充说明:

    1. 不到无可奈何,不要容许复杂请求,服务器压力倍增;
    2. 尽可能不发复杂请求,能够选择将参数放到url


另外,也能够经过安装这个插件来处理CORS请求:https://github.com/ottoyiu/django-cors-headers

遇到的问题

我测试是从8000端口往9000端口发送请求。对于CORS,若是从8000端口往非9000端口发送请求,若是不设置响应头,错误信息以下(设置了CORS响应头后便可正常通讯):

Failed to load http://127.0.0.1:8050/get_data.html: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:8000' is therefore not allowed access.

可是若是从8000端口往9000端口发请求,则不管如何设置CORS都不行(安装插件也是),浏览器错误提示也多了一个重定向:

Failed to load http://127.0.0.1:9000/get_data.html: Redirect from 'http://127.0.0.1:9000/get_data.html' to 'http://127.0.0.1:9000/get_data.html/' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:8000' is therefore not allowed access.

我浏览器是chrome v61。若是你们有这方面的经验,欢迎指教,谢谢!


补充,通过屡次测试,一样的条件用微软的Edge浏览器,居然能够通讯。。。而后在chrome浏览器下,换用了非9000端口(试了900一、9020都没毛病),也能够通讯。搞不明白,chrome和9000端口有什么仇,

总之,个人心里是崩溃的。。。


总结

JSONP 和 CORS都是经过浏览器, requests模块经过后台,所以服务器压力大。

经过CORS,客户端不须要做任何改变。而且支持更多请求方式,是将来的趋势。

JSONP兼容性比CORS好,老式浏览器对CORS支持可能不太好。

ajax的JS实现

XMLHttpRequest

ajax的全部操做都是基于XMLHttpRequest对象,用于在后台与服务器交换数据。这意味着能够在不从新加载整个网页的状况下,对网页的某部分进行更新。

建立XMLHttpRequest 对象

var xmlHttp = new XMLHttpRequest();

全部的现代浏览器都内建了XMLHttpRequest 对象,可是IE7之前的版本有所不一样。为了照顾浏览器的兼容性问题,能够用如下方式来建立XMLHttpRequest对象:
方式一:判断浏览器是否支持XMLHttpRequest 对象

var xmlhttp;
if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
  xmlhttp=new XMLHttpRequest();
  }
else
  {// code for IE6, IE5
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }

方式二:基于异常捕捉的方式

function createXMLHttpRequest() {
        var xmlHttp;
        // 适用于大多数浏览器,以及IE7和IE更高版本
        try{
            xmlHttp = new XMLHttpRequest();
        } catch (e) {
            // 适用于IE6
            try {
                xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
            } catch (e) {
                // 适用于IE5.5,以及IE更早版本
                try{
                    xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
                } catch (e){}
            }
        }            
        return xmlHttp;
    }

使用流程

创建链接

var url = “/ajax_handler”;
var params = “lorem=ipsum&name=binny”;

open(method, url, async):

var xmlHttp = createXMLHttpRequest();
xmlHttp.open("GET", url+"?"+params, true); 

method请求方法,GET/POST;url, 请求的服务端路径;async参数能够不给,默认是true,执行异步请求

发送请求

xmlHttp.send(null);

对于GET方式来讲,没有请求体,参数是放在在url的”?”后面,即查询字符串;出于兼容性考虑,若是send()方法没有参数,建议给出null。对于POST方式,参数要放在send()方法中,这个下面再说。

接收响应

onreadystatechange 事件
该事件在XMLHttpRequest对象的状态发生变化时被调用。XMLHttpRequest有五种状态:
- 0:建立了XMLHttpRequest对象;
- 1:open()方法创建链接;
- 2:send()方法发送数据;
- 3:读取服务器响应开始;
- 4:读取服务器响应结束;
事件会在状态1 - 4 时触发:
readyState属性,获取对象状态
该属性用来获取XMLHttpRequest对象的状态,下面的alert会执行4次,并显示每次的状态:

xmlHttp.onreadystatechange = function() {
            alert(xmlHttp.readyState);
        };

status属性,获取响应状态码
服务器响应状态码若是是200,表示请求被成功处理,这一般也是咱们所关心的。经过XMLHttpRequest对象的status属性,能够获取服务器的状态码
responseText属性,获取响应内容

xmlHttp.onreadystatechange = function() {
            if(xmlHttp.readyState == 4 && xmlHttp.status == 200) { alert(xmlHttp.responseText); }
        };

在XMLHttpRequest中使用POST方法

不一样于GET方式,POST请求时,没有查询字符串,参数放到send()方法内,也就是请求体。
并且,须要设置请求头:

var url = "/ajax_handler";
var params = "lorem=ipsum&name=binny";

var xmlHttp = new XMLHttpRequest();
xmlHttp .open("POST", url, true);

//设置请求头 setRequestHeader(key, value)
xmlHttp .setRequestHeader("Content-type", "application/x-www-form-urlencoded");
// 注意 :form表单会默认"Content-type"键值对不设定,会致使Web服务器忽略请求体的内容,所以必须加上上面这行
// 表示客户端提交给服务器文本内容的编码方式是URL编码,即除了标准字符外,每字节以双字节16进制前加个“%”表示
xmlHttp .setRequestHeader("Content-length", params.length);
xmlHttp .setRequestHeader("Connection", "close");

xmlHttp .send(params);

xmlHttp .onreadystatechange = function() {
    if(http.readyState == 4 && http.status == 200) {
        alert(http.responseText);
    }
}

为何要设置请求头

由于客户端与服务端的通讯使用HTTP协议,HTTP协议规定,客户端发出的请求,必须有请求头,用来告诉
服务器客户端要的资源及相关的参数。XMLHttpRequest对象遵循HTTP协议,所以须要发送请求头给服务器。
可是 XMLHttpRequest默认的状况下有些参数可能没有说明在HTTP头里,好比提交form表单时,是没有”Content-type”这个键和值的。所以咱们须要经过XMLHttpRequest提供的setRequestHeader 方法,来手动添加。

发送二进制数据

参考Django中static & media的简单配置及图片上传实践