<前端技术>Ajax解析

时间:Aug. 31, 2017 分类:

目录:

ajax简介

对于web程序,通过浏览器接收并处理请求,然后返回结果,往往返回的是字符串(html),并由浏览器进行渲染,而一个操作需要加载所有的页面,而ajax全称为Asynchronous JavaScript and XML (异步的JavaScript和XML),一种创建交互式网页应用的网页开发技术方案,可以实现请求后对局部页面进行刷新。

可以实现注册的时候检查用户是否存在,登录时提醒用户的用户名密码错误等操作。

ajax的三种实现方式

原生ajax

概述

原生的ajax通过浏览器提供的对象XMLHttpRequest进行请求的操作,像IE5,IE6的浏览器并没有,不过现在主流的浏览器中都提供该对象。

XMLHttpRequest对象主要方法

创建请求

void open(String method, String url, Boolen async)

参数介绍

  • method: 请求方式(字符串类型),例如POST,GET,DELETE
  • url: 要请求的地址(字符串类型)
  • asyne:是否异步(布尔类型)

发送请求

void send(String body)

参数介绍

  • body:要发送的数据(字符串类型)

设置请求头

void setRequestHeader(String header, String value)

在POST请求的时候需要设置请求头,而GET请求不需要

参数介绍

  • header: 请求头的key(字符串类型)
  • value:请求头的value(字符串类型)

获取响应头

getAllResponseHeaders()
  • 返回值: 响应头数据(字符串类型)
getResponseHeader(String header)

参数介绍

  • header:响应头的key(字符串类型)

返回值

  • 响应头中指定的header对应的值

终止请求

abort()

XMLHttpRequest对象主要属性

状态值

Number readyState

详细值:

  • 0:未初始化,尚未调用open()方法
  • 1:启动,调用了open()方法,为调用send()方法
  • 2:发送,已经调用了send()方法,未接收到响应
  • 3:接收,已经接收了部分响应数据
  • 4:完成,已经接收了全部响应数据

当readyState的值发生改变的时候自动触发的函数(回调函数)

Function onreadystatechange

服务器返回的数据(字符串类型)

String responseText

服务器返回的数据(XML类型)

XmlDocument responseXML

状态码

Number states

状态码,例如200,404等

通过tornado实现ajax

html代码

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input id="username" type="text" name="username" />
    <input id="passwd" type="password" name="passwd" />
    <input type="button" value="提交" onclick="SubmitForm();" />
    <script>
        function SubmitForm() {
            xhr = new XMLHttpRequest();                         //创建XMLHttpRequest对象
            xhr.open("POST", "/login", true);                   //创建请求
            xhr.onreadystatechange = Func;                      //状态值改变的回调函数
            xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded; charset=UTF-8")        //设置响应头
            xhr.send("username=" + document.getElementById('username').value + ";passwd=" + document.getElementById('passwd').value)    //发送请求
        }
        function Func() {
            if(xhr.readyState == 4){                            //根据状态值判断是否接收到响应数据
                console.log(1)
                var data = xhr.responseText;                    //获取响应数据
                var ret_dict = JSON.parse(data);
                if(ret_dict.status){
                                                                //pass
                }else{
                    alert(ret_dict.message)
                }
            }
        }
    </script>
</body>
</html>

toanado代码

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import tornado.ioloop                               #导入模块
import tornado.web

class LoginHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("login.html")
    def post(self, *args, **kwargs):
        dic = {"status": True, "message": ""}
        username = self.get_argument('username',None)
        passwd = self.get_argument('passwd',None)

        if username == 'why' and passwd == '123456':
            pass
        else:
            dic['status'] = False
            dic['message'] = "用户名密码错误"
        import json
        self.write(json.dumps(dic))                 #返回字符串


application = tornado.web.Application([             #创建tornado.web.Application对象
    (r"/login", LoginHandler),
], )                                      #加载settings

if __name__ == "__main__":
    application.listen(8888)                        #创建一个socket
    tornado.ioloop.IOLoop.instance().start()        #使用epoll,IO多路复用循环

如果想跨浏览器支持,可以先定义

function GetXHR(){
            var xhr = null;
            if(XMLHttpRequest){
                xhr = new XMLHttpRequest();
            }else{
                xhr = new ActiveXObject("Microsoft.XMLHTTP");
            }
            return xhr;

        }

然后创建对象的时候通过以下方式创建

var xhr = GetXHR();

JQuery提供的ajax实现

JQuery的2.x版本默认是不支持IE9以下的浏览器版本,如果想要支持IE5和6的话,需要使用JQuery1.x

        function SubmitForm() {
            $.post('/login',{'username': $('#username').val(), 'passwd': $('#passwd').val()}, function(callback){
                console.log(callback);
            });
        }

方法解析

$.get({
    url: '地址',    
    data: {'k1':'v1',......},
    success: function('返回字符串'){
        方法内容
    }
})

浏览器默认返回的是字符串,需要对其返回结果进行特定格式的解析,例如Json的就需要执行JSON.parse(),不过也可以直接通过执行dataType指定返回结果的解析方式。

$.get({
    url: '地址',                        //待载入页面的URL地址
    data: {'k1':'v1',......},       //待发送 Key/value 参数。
    dataType: "json"   //返回内容格式,xml, json,  script, text, html
    success: function('返回字符串'){        //载入成功时回调函数。
        方法内容
    }
})

如果dataType指定为script,内部就会为其执行一个eval方法。

而还有一些方法,例如getJson()就是在get的基础上直接指定了dataType: "json",同理还有getScript()

但是这些操作最后都是调用ajax方法

                部分参数:

                        url:请求地址
                       type:请求方式,GET、POST(1.9.0之后用method)
                    headers:请求头
                       data:要发送的数据
                contentType:即将发送信息至服务器的内容编码类型(默认: "application/x-www-form-urlencoded; charset=UTF-8")
                      async:是否异步
                    timeout:设置请求超时时间(毫秒)

                 beforeSend:发送请求前执行的函数(全局)
                   complete:完成之后执行的回调函数(全局)
                    success:成功之后执行的回调函数(全局)
                      error:失败之后执行的回调函数(全局)


                    accepts:通过请求头发送给服务器,告诉服务器当前客户端课接受的数据类型
                   dataType:将服务器端返回的数据转换成指定类型
                                   "xml": 将服务器端返回的内容转换成xml格式
                                  "text": 将服务器端返回的内容转换成普通文本格式
                                  "html": 将服务器端返回的内容转换成普通文本格式,在插入DOM中时,如果包含JavaScript标签,则会尝试去执行。
                                "script": 尝试将返回值当作JavaScript去执行,然后再将服务器端返回的内容转换成普通文本格式
                                  "json": 将服务器端返回的内容转换成相应的JavaScript对象
                                 "jsonp": JSONP 格式
                                          使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数

                                  如果不指定,jQuery 将自动根据HTTP包MIME信息返回相应类型(an XML MIME type will yield XML, in 1.4 JSON will yield a JavaScript object, in 1.4 script will execute the script, and anything else will be returned as a string

                 converters: 转换器,将服务器端的内容根据指定的dataType转换类型,并传值给success回调函数
                         $.ajax({
                              accepts: {
                                mycustomtype: 'application/x-some-custom-type'
                              },

                              // Expect a `mycustomtype` back from server
                              dataType: 'mycustomtype'

                              // Instructions for how to deserialize a `mycustomtype`
                              converters: {
                                'text mycustomtype': function(result) {
                                  // Do Stuff
                                  return newresult;
                                }
                              },
                            });

iframe实现伪ajax

html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
        <div>
            <p>请输入要加载的地址:<span id="currentTime"></span></p>
            <p>
                <input id="url" type="text" />
                <input type="button" value="刷新" onclick="LoadPage();">
            </p>
        </div>
        <div>
            <h3>加载页面位置:</h3>
            <iframe id="iframePosition" style="width: 100%;height: 500px;"></iframe>
        </div>
        <script type="text/javascript">
            window.onload= function(){
                var myDate = new Date();
                document.getElementById('currentTime').innerText = myDate.getTime();

            };
            function LoadPage(){
                var targetUrl =  document.getElementById('url').value;
                document.getElementById("iframePosition").src = targetUrl;
            }
        </script>
</body>
</html>

直接访问即可,当我们刷新页面的时候,当前时间是变动的

而当我们输入网页进行请求的时候

可以看到时间是没有进行改变的,所以是一个局部的请求,并没有重新加载所有的页面

甚至可以把页面链接过来成为自己的网站.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        body{
            margin: 0px;
        }
    </style>
</head>
<body>
<iframe id="iframePosition" style="width: 100%;height: 1000px" src="http://www.whysdomain.com"></iframe>
</body>
</html>

不过还是有个滑动窗口

通过ajax上传文件

tornado代码

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import tornado.ioloop                               #导入模块
import tornado.web

class IndexHandler(tornado.web.RequestHandler):   #继承tornado.web.RequestHandler
    def get(self):                                  #直接访问的时候
        self.render("index.html")    #返回html
    def post(self,*args,**kwargs):
        file = self.request.files["f"]
        for meta in file:
            file_name = meta['filename']
            with open(file_name,"wb") as up:
                up.write(meta['body'])
        self.render("index.html")

settings = {
    'template_path': 'template',                    #配置模板路径
    'static_path': 'static',                        #静态文件路径
}

application = tornado.web.Application([             #创建tornado.web.Application对象
    (r"/index",IndexHandler),                       #当发送url请求的时候,对检查是否匹配,然后路由到对应的类的get,post等方法(路由映射)
], **settings)                                      #加载settings

if __name__ == "__main__":
    application.listen(8888)                        #创建一个socket
    tornado.ioloop.IOLoop.instance().start()        #使用epoll,IO多路复用循环

html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/index" method="POST" enctype="multipart/form-data">
    <input type="file" name="f">
    <input type="submit" value="提交">
</form>
</body>

注意这里要加multipart/form-data

如果要指定路径,post方法可以进行一下修改

    def post(self,*args,**kwargs):
        file = self.request.files["f"]
        for meta in file:
            file_name = meta['filename']
            import os
            with open(os.path.join('statics','img',file_name),"wb") as up:
                up.write(meta['body'])
        self.render("index.html")

代码目录

通过原生ajax

html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/index" method="POST" enctype="multipart/form-data">
    <input type="file" id="img" name="f">
    <input type="button" value="提交" onclick="UploadFile();">
</form>
<script>
    function UploadFile(){
        var fileObj = document.getElementById("img").files[0];
        var form = new FormData();
        form.append("f", fileObj);//如果有更多的其他的key,value也可以添加
        var xhr = new XMLHttpRequest();
        xhr.open("post","/index",true);
        xhr.send(form)
    }
</script>
</body>
</html>

使用JQuery的ajax提交

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/index" method="POST" enctype="multipart/form-data">
    <input type="file" id="img" name="f">
    <input type="button" value="提交" onclick="UploadFile2();">
</form>
<script src="http://image.whysdomain.com/static/jquery-1.8.2.js"></script>
<script>
    function UploadFile(){
        var fileObj = document.getElementById("img").files[0];
        var form = new FormData();
        form.append("f", fileObj);//如果有更多的其他的key,value也可以添加
        var xhr = new XMLHttpRequest();
        xhr.open("post","/index",true);
        xhr.send(form)
    }
    function UploadFile2(){
        var fileObj = $("#img")[0].files[0];
        var form = new FormData();
        form.append("f", fileObj); //如果有更多的其他的key,value也可以添加
        $.ajax({
            type: 'POST',
            url: '/index',
            data: form,
            processData: false, //JQuery不处理数据
            contentType: false ,//JQuery不设置内容类型
        })
    }
</script>
</body>
</html>

通过伪ajax

html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .hide{
            display: none;
        }
    </style>
</head>
<body>
<form action="/index" id="my_form" method="POST" enctype="multipart/form-data">
    <input type="file" id="img" name="f">
    <input type="button" value="提交" onclick="UploadFile3();">
    <iframe id="my_iframe" name="my_iframe" src="" class="hide"></iframe>
</form>
<script src="http://image.whysdomain.com/static/jquery-1.8.2.js"></script>
<script>
    function UploadFile(){
        var fileObj = document.getElementById("img").files[0];
        var form = new FormData();
        form.append("f", fileObj);//如果有更多的其他的key,value也可以添加
        var xhr = new XMLHttpRequest();
        xhr.open("post","/index",true);
        xhr.send(form)
    }
    function UploadFile2(){
        var fileObj = $("#img")[0].files[0];
        var form = new FormData();
        form.append("f", fileObj); //如果有更多的其他的key,value也可以添加
        $.ajax({
            type: 'POST',
            url: '/index',
            data: form,
            processData: false, //JQuery不处理数据
            contentType: false ,//JQuery不设置内容类型
        })
    }
    function UploadFile3(){
        document.getElementById('my_iframe').onload = Testt;
        document.getElementById('my_form').target = 'my_iframe';//提交到iframe
        document.getElementById('my_form').submit();
        }
    function Testt(ths){
            var t = $("#my_iframe").contents().find("body").text();
            console.log(t);
            }
</script>
</body>
</html>

ajax跨域请求

对于ajax请求,如果跨域名,浏览器默认是不允许的

在hosts文件中修改一下把本机127.0.0.1绑定一个域名www.why.com和www.wanghongyu.com进行模拟。

为什么会出现跨域

跨域问题来源于JavaScript的同源策略,即只有"协议+主机名+端口号(如存在)"相同,则允许相互访问。也就是说JavaScript只能访问和操作自己域下的资源,不能访问和操作其他域下的资源。跨域问题是针对JS和ajax的,html本身没有跨域问题,比如a标签、script标签、甚至form标签(可以直接跨域发送数据并接收数据)等

主域名不同,子域名不同,端口不同,协议不同都不能机型跨域

用于发送跨域ajax请求的web server

tornado代码

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import tornado.ioloop                               #导入模块
import tornado.web


class KuayuOneHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render('kuayu.html')

settings = {
    'template_path': 'template',                    #配置模板路径
    'static_path': 'static',                        #静态文件路径
}

application = tornado.web.Application([             #创建tornado.web.Application对象
    (r"/kuayuone",KuayuOneHandler),
], **settings)                                      #加载settings

if __name__ == "__main__":
    application.listen(8888)                        #创建一个socket
    tornado.ioloop.IOLoop.instance().start()        #使用epoll,IO多路复用循环

html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="button" value="提交" onclick="DoAjax();">
    <script src="http://image.whysdomain.com/static/jquery-1.8.2.js"></script>
    <script>
    function DoAjax(){
        $.ajax({
            url: 'http://www.why.com:8889/kuayutwo',
            type: 'POST',
            data: "{'k1','v1'}",
            success: function(arg){
                console.log(arg);
            }
        })
    }
    </script>
</body>
</html>

用于接收跨域ajax请求的web server

tornado代码

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import tornado.ioloop                               #导入模块
import tornado.web


class KuayuTwoHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.write('why kuayu2')
    def post(self, *args, **kwargs):
        self.write('why kuayu2')

settings = {
    'template_path': 'template',                    #配置模板路径
    'static_path': 'static',                        #静态文件路径
}

application = tornado.web.Application([             #创建tornado.web.Application对象
    (r"/kuayutwo",KuayuTwoHandler),                    #当发送url请求的时候,对检查是否匹配,然后路由到对应的类的get,post等方法(路由映射)
], **settings)                                      #加载settings

if __name__ == "__main__":
    application.listen(8889)                        #创建一个socket
    tornado.ioloop.IOLoop.instance().start()        #使用epoll,IO多路复用循环

进行跨域ajax请求

用于接收ajax请求的服务器

用于发送ajax请求的服务器

当点击提交后会发送跨域的ajax请求,从www.wanghongyu.com发送到www.why.com

在console中可以看到提交失败

报错信息为:

XMLHttpRequest cannot load http://www.why.com:8889/kuayutwo. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://www.wanghongyu.com:8888' is therefore not allowed access.

可以看到报错是禁止跨域请求的,报错的位置是在浏览器接收到结果的时候,请求可以进行发送,并且能接收到返回的结果,由于浏览器的同源策略,不是本域名的,所以报错。

如果想进行跨域ajax请求,有两种方式,一种是以jsonp的方式实现,另一种是接收端设置响应人实现。

通过jsonp方式实现跨域ajax

对于jsonp,是使用了代码块的src属性,带有src属性的代码块都可以跨域进行请求,例如iframe,img,script等,jsonp就是使用的script

原生ajax实现

html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="button" value="提交" onclick="JsonpAjax();">
    <script src="http://image.whysdomain.com/static/jquery-1.8.2.js"></script>
    <script>
    function JsonpAjax(){
        var tag = document.createElement('script');
        tag.src = "http://www.why.com:8889/kuayutwo";
        document.head.appendChild(tag);
        document.head.removeChild(tag);
    }
    </script>
</body>
</html>

创建一个script标签,标签中src为需要请求的网站,当执行的时候就进行了一次get请求,注意只能是get请求

然后就能看到ajax的请求结果了,刚才是使用原生的ajax实现的

JQuery实现

html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="button" value="提交" onclick="JsonpAjax();">
    <script src="http://image.whysdomain.com/static/jquery-1.8.2.js"></script>
    <script>
    function JsonpAjax(){
        $.ajax({
            url: "http://www.why.com:8889/kuayutwo",
            dataType: 'jsonp',
            jsonp: 'callback',
            jsonpCallBack: 'func'
        })
    }
    function func(dict){
        console.log(dict);
    }
    </script>
</body>
</html>

接收跨域ajax请求的web server进行修改

tornado代码

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import tornado.ioloop                               #导入模块
import tornado.web


class KuayuTwoHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.write('func([11,22,33]);')
    def post(self, *args, **kwargs):
        self.write('why kuayu2')

settings = {
    'template_path': 'template',                    #配置模板路径
    'static_path': 'static',                        #静态文件路径
}

application = tornado.web.Application([             #创建tornado.web.Application对象
    (r"/kuayutwo",KuayuTwoHandler),                    #当发送url请求的时候,对检查是否匹配,然后路由到对应的类的get,post等方法(路由映射)
], **settings)                                      #加载settings

if __name__ == "__main__":
    application.listen(8889)                        #创建一个socket
    tornado.ioloop.IOLoop.instance().start()        #使用epoll,IO多路复用循环

这样就实现了跨域ajax请求

不过,接收ajax请求的服务器返回的方法为func,所以发送ajax请求的服务器只能使用func方法作为回调函数。

其实在发送请求的时候callback是一个参数被传递到服务器的。

回调函数发送名作为参数

html代码

这里只是修改了回调函数的方法名

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="button" value="提交" onclick="JsonpAjax();">
    <script src="http://image.whysdomain.com/static/jquery-1.8.2.js"></script>
    <script>
    function JsonpAjax(){
        $.ajax({
            url: "http://www.why.com:8889/kuayutwo",
            dataType: 'jsonp',
            jsonpCallback: 'aaa'
        })
    }
    function aaa(dict){
        console.log(dict);
    }
    </script>
</body>
</html>

tornado代码

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import tornado.ioloop                               #导入模块
import tornado.web


class KuayuTwoHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        callback = self.get_argument('callback')
        print callback
        self.write('%s([11,22,33]);' % callback)
    def post(self, *args, **kwargs):
        self.write('why kuayu2')

settings = {
    'template_path': 'template',                    #配置模板路径
    'static_path': 'static',                        #静态文件路径
}

application = tornado.web.Application([             #创建tornado.web.Application对象
    (r"/kuayutwo",KuayuTwoHandler),                    #当发送url请求的时候,对检查是否匹配,然后路由到对应的类的get,post等方法(路由映射)
], **settings)                                      #加载settings

if __name__ == "__main__":
    application.listen(8889)                        #创建一个socket
    tornado.ioloop.IOLoop.instance().start()        #使用epoll,IO多路复用循环

可以看到请求的时候是把callback当做一个参数传递给了服务端

以上是使用最多的一种方法。

通过响应头

第二种方法是需要浏览器的版本,实现的方式是在返回数据的时候加入响应头,通过cors跨域资源共享来实现。

以下代码在回到第一种方式的初始代码后的操作

还是原来的报错

XMLHttpRequest cannot load http://www.why.com:8889/kuayutwo. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://www.wanghongyu.com:8888' is therefore not allowed access.

可以看到报错是禁止跨域请求的,报错的位置是在浏览器接收到结果的时候,请求可以进行发送,并且能接收到返回的结果,由于浏览器的同源策略,不是本域名的,所以报错。我们在请求头中添加该参数即可。

设置响应头实现ajax跨域请求

在允许跨请求的响应头添加域名

class KuayuTwoHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.write('why kuayu2')
    def post(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin','http://www.wanghongyu.com:8888')
        self.write('why kuayu2')

如果过多url可以使用self.set_header('Access-Control-Allow-Origin','http://www.wanghongyu.com:8888,http://www.whysdomain.com'),也可以用*来允许全部。

不过,这是对于简单请求。

简单请求和非简单请求

区分条件:

    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

非简单请求的跨域ajax请求

初始准备

html中由post换为put

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="button" value="提交" onclick="DoAjax();">
    <script src="http://image.whysdomain.com/static/jquery-1.8.2.js"></script>
    <script>
    function DoAjax(){
        $.ajax({
            url: 'http://www.why.com:8889/kuayutwo',
            type: 'PUT',
            data: "{'k1','v1'}",
            success: function(arg){
                console.log(arg);
            }
        })
    }
    </script>
</body>
</html>

tornado代码

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import tornado.ioloop                               #导入模块
import tornado.web


class KuayuTwoHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.write('why kuayu2')
    def post(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin','http://www.wanghongyu.com:8888')
        self.write('why kuayu2')
    def put(self, *args, **kwargs):
        self.write('why kuayu2')

settings = {
    'template_path': 'template',                    #配置模板路径
    'static_path': 'static',                        #静态文件路径
}

application = tornado.web.Application([             #创建tornado.web.Application对象
    (r"/kuayutwo",KuayuTwoHandler),                    #当发送url请求的时候,对检查是否匹配,然后路由到对应的类的get,post等方法(路由映射)
], **settings)                                      #加载settings

if __name__ == "__main__":
    application.listen(8889)                        #创建一个socket
    tornado.ioloop.IOLoop.instance().start()        #使用epoll,IO多路复用循环

可以看到

OPTIONS http://www.why.com:8889/kuayutwo 405 (Method Not Allowed)

没有options方法报错405

服务端添加options方法

tornado代码

class KuayuTwoHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.write('why kuayu2')
    def post(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin','http://www.wanghongyu.com:8888')
        self.write('why kuayu2')
    def options(self, *args, **kwargs):
        self.write('why kuayu2')
    def put(self, *args, **kwargs):
        self.write('why kuayu2')

再次进行跨域ajax请求

报错信息为:

XMLHttpRequest cannot load http://www.why.com:8889/kuayutwo. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://www.wanghongyu.com:8888' is therefore not allowed access.

还是因为跨域请求没有允许

添加option方法的允许访问域名

class KuayuTwoHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.write('why kuayu2')
    def post(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin','http://www.wanghongyu.com:8888')
        self.write('why kuayu2')
    def options(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin','*')
        self.write('why kuayu2')
    def put(self, *args, **kwargs):
        self.write('why kuayu2')

报错信息

MLHttpRequest cannot load http://www.why.com:8889/kuayutwo. Method PUT is not allowed by Access-Control-Allow-Methods in preflight response.

这是因为虽然返回了预检信息,但是在options中没有允许put方法

添加允许put方法

class KuayuTwoHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.write('why kuayu2')
    def post(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin','http://www.wanghongyu.com:8888')
        self.write('why kuayu2')
    def options(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin','*')
        self.set_header('Access-Control-Allow-Methods','PUT')
        self.write('why kuayu2')
    def put(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin','*')
        self.write('why kuayu2')

并且这次我们先允许了put的方法的跨域ajax请求

对于自定义请求头

对于自定义的请求头进行跨域的ajax请求的时候,也是需要预检的

设置一个请求头

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="button" value="提交" onclick="DoAjax();">
    <script src="http://image.whysdomain.com/static/jquery-1.8.2.js"></script>
    <script>
    function DoAjax(){
        $.ajax({
            url: 'http://www.why.com:8889/kuayutwo',
            type: 'PUT',
            headers : {'h1':'why'},
            data: "{'k1','v1'}",
            success: function(arg){
                console.log(arg);
            }
        })
    }
    </script>
</body>
</html>

进行跨域请求

报错信息:

XMLHttpRequest cannot load http://www.why.com:8889/kuayutwo. Request header field h1 is not allowed by Access-Control-Allow-Headers in preflight response.

这是在没有允许自定义的请求头

设置允许自定义请求头

class KuayuTwoHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.write('why kuayu2')
    def post(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin','http://www.wanghongyu.com:8888')
        self.write('why kuayu2')
    def options(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin','*')
        self.set_header('Access-Control-Allow-Methods','PUT')
        self.set_header('Access-Control-Allow-Headers','h1')
        self.write('why kuayu2')
    def put(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin','*')
        self.write('why kuayu2')

预检信息超时时间

self.set_header('Access-Control-Max-Age', 10)

设置预检的过期时间,10s内再次发送请求的时候不会进行预检。

携带cookies进行跨域ajax

ajax内要添加

xhrFields:{withCredentials: true},

而options和put中都需要加入

self.set_header('Access-Control-Allow-Credenttials','k1')
和
self.set_header('Access-Control-Allow-Origin','http://www.wanghongyu.com:8888')

注意这个Access-Control-Allow-Origin一定不能是*,否则会报

报错信息

XMLHttpRequest cannot load http://www.why.com:8889/kuayutwo. Response to preflight request doesn't pass access control check: A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true. Origin 'http://www.wanghongyu.com:8888' is therefore not allowed access. The credentials mode of an XMLHttpRequest is controlled by the withCredentials attribute.

没有允许Access-Control-Allow-Credenttials的话

报错信息

XMLHttpRequest cannot load http://www.why.com:8889/kuayutwo. Response to preflight request doesn't pass access control check: Credentials flag is 'true', but the 'Access-Control-Allow-Credentials' header is ''. It must be 'true' to allow credentials. Origin 'http://www.wanghongyu.com:8888' is therefore not allowed access.

以下是成功进行请求的代码

html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="button" value="提交" onclick="DoAjax();">
    <script src="http://image.whysdomain.com/static/jquery-1.8.2.js"></script>
    <script>
    function DoAjax(){
        $.ajax({
            url: 'http://www.why.com:8889/kuayutwo',
            type: 'PUT',
            headers : {'h1':'why'},
            data: "{'k1','v1'}",
            xhrFields:{withCredentials: true},
            success: function(arg){
                console.log(arg);
            }
        })
    }
    </script>
</body>
</html>

tornado代码

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import tornado.ioloop                               #导入模块
import tornado.web


class KuayuTwoHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.write('why kuayu2')
    def post(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin','http://www.wanghongyu.com:8888')
        self.write('why kuayu2')
    def options(self, *args, **kwargs):
        self.set_header('Access-Control-Allow-Origin','http://www.wanghongyu.com:8888')
        self.set_header('Access-Control-Allow-Methods','PUT')
        self.set_header('Access-Control-Allow-Headers','h1')
        self.set_header('Access-Control-Allow-Credentials','k1')
        self.write('why kuayu2')
    def put(self, *args, **kwargs):
        self.set_cookie('k1','wanghongyu')
        self.set_header('Access-Control-Allow-Origin','http://www.wanghongyu.com:8888')
        self.set_header('Access-Control-Allow-Credentials','k1')
        self.write('why kuayu2')



settings = {
    'template_path': 'template',                    #配置模板路径
    'static_path': 'static',                        #静态文件路径
}

application = tornado.web.Application([             #创建tornado.web.Application对象
    (r"/kuayutwo",KuayuTwoHandler),                    #当发送url请求的时候,对检查是否匹配,然后路由到对应的类的get,post等方法(路由映射)
], **settings)                                      #加载settings

if __name__ == "__main__":
    application.listen(8889)                        #创建一个socket
    tornado.ioloop.IOLoop.instance().start()        #使用epoll,IO多路复用循环