Web基础(7)Tornado、Cookie、Session、Ajax、文件上传

1. tornado框架的简单配置与应用

# -*-coding:utf-8-*-
import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        # self.write("Hello World")
        self.render("s1.html")
    def post(self):
        self.write("Hello World")

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



# 路由系统,或路由映射
application = tornado.web.Application([
    (r"/index", MainHandler)
], **settings)

if __name__ == '__main__':
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

Tornado程序

基本步骤:

继承类

application

run

配置文件

模板路径


2.  使用tornado的模板语言

# -*-coding:utf-8-*-
import tornado.ioloop
import tornado.web

INPUTS_LIST = []

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        # self.write("Hello World")
        # 1. 打开s1.html文件,读取内容(包含特殊语法)
        # 2. numbers = [1, 2, 3, 4, 5] && 读取内容(包括特殊语法)
        # 3. 得到新的字符串
        # 4. 返回给用户
        self.render("s1.html", names=INPUTS_LIST)
    def post(self, *args, **kwargs):
        name = self.get_argument("txt")
        print(name)
        INPUTS_LIST.append(name)
        self.render("s1.html", names=INPUTS_LIST)

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



# 路由系统,或路由映射
application = tornado.web.Application([
    (r"/index", MainHandler)
], **settings)

if __name__ == '__main__':
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="static/commons.css" rel="stylesheet">

</head>
<body>
    <h1>H提交内容:</h1>
    <form method="post" action="/index">
        <input tyoe="text" name="txt">
        <input type="submit" value="提交">
    </form>
    <h1>展示内容:</h1>
    <ul>
        {% for item in names%}
        <li>{{item}}</li>
        {% end %}
    </ul>
</body>
</html>

self.get_argument("txt") # 获取用户提交的数据



{% for item in names%}

<li>{{item}}</li>

{% end %} # 模板语言里通过for循环展示数据



3. 模板语言:

{{}}

{% if %} {% end %}

自定义函数、类:uimethod uimodule

代码案例:

# -*-coding:utf-8-*-

import tornado.ioloop
import tornado.web
import uimethod as mt
import uimodule as md



INPUTS_LIST = []

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        # self.write("Hello World")
        # 1. 打开s1.html文件,读取内容(包含特殊语法)
        # 2. numbers = [1, 2, 3, 4, 5] && 读取内容(包括特殊语法)
        # 3. 得到新的字符串
        # 4. 返回给用户
        self.render("s1.html", names=INPUTS_LIST)
    def post(self, *args, **kwargs):
        name = self.get_argument("txt", None)
        if name:
            INPUTS_LIST.append(name)
        print(name)
        self.render("s1.html", names=INPUTS_LIST)

settings = {
    "template_path": "template", # 模板路径的配置
    "static_path": "static", # 静态文件路径
    "ui_methods": mt,
    "ui_modules": md,
}

# 路由系统,或路由映射
application = tornado.web.Application([
    (r"/index", MainHandler)
], **settings)

if __name__ == '__main__':
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link href="static/commons.css" rel="stylesheet">

</head>
<body>
    <h1>H提交内容:</h1>
    <form method="post" action="/index">
        <input tyoe="text" name="txt">
        <input type="submit" value="提交">
    </form>
    <h1>展示内容:</h1>
    <h2>{{ func("a") }}</h2>
    <h3>{% module custom() %}</h3>
    <ul>
        {% for item in names%}
            {% if item == "Steve" %}
                <li style="color:red">{{item}}</li>
            {% else %}
                <li>{{item}}</li>
            {% end %}
        {% end %}
    </ul>
</body>
</html>
# -*-coding:utf-8-*-
# uimethod
def func(self, arg):
    print(arg)
    return "Method"
# -*-coding:utf-8-*-
# uimodule
from tornado.web import UIModule
from tornado import escape


class custom(UIModule):
    def render(self, *args, **kwargs):
        return "Module"



4. 内置模板方法

Tornado在模板中默认提供了一些函数、字段、类以供模板使用:

escape:tornado.escape.xhtml_escape 的别名

xhtml_escape:tornado.escape.xhtml_escape 的别名

url_escape:tornado.escape.url_escape 的别名

json_encode:tornado.escape.json_encode 的别名

squeeze:tornado.escape.squeeze 的别名

linkify:tornado.escape.linkify 的别名

datetime:Python的datetime模块

handler:当前的RequestHandler对象

request:handler.request的别名

current_user:handler.current_user 的别名

locale:handler.locale 的别名

_: handler.locale.translate 的别名

static_url: handler.static_url 的别名

xsrf_form_html:handler.xsrf_form_html 的别名

Tornado默认提供的这些功能其本质上就是UIMethod和UIModule。


5. Tornado之cookie

代码案例:

# -*-coding:utf-8-*-

import tornado.ioloop
import tornado.web



INPUTS_LIST = []

class IndexHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render("index.html", )

class ManageHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        cookie = self.get_cookie("auth")
        if cookie == "1":
            self.render("/manage.html")
        else:
            self.redirect("/login")


class LoginHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render("login.html", status_text="")

    def post(self, *args, **kwargs):
        username = self.get_argument("username", None)
        pwd = self.get_argument("password", None)
        if username == "Steve" and pwd == "123":
            # 表示要设定cookie值
            self.set_cookie("auth", "1", expires=10) # expires后面的参数为有效期限
            self.redirect("/manage")
        else:
            self.render("login.html", status_text="登录失败")

class LogoutHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.set_cookie("auth", 0)
        self.redirect("/login")

settings = {
    "template_path": "views", # 模板路径的配置
}

# 路由系统,或路由映射
application = tornado.web.Application([
    (r"/index", IndexHandler),
    (r"/login", LoginHandler),
    (r"/manage", ManageHandler),
    (r"/logout", LogoutHandler)
], **settings)

if __name__ == '__main__':
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/login" method="post">
        <input type="text" name="username">
        <input type="password" name="password">
        <input type="submit" value="登录">
        <span style="color:red">{{status_text}}</span>
    </form>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <a href="/logout">退出</a>
    <h1>您的数据</h1>
    <h2>身高:170cm</h2>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>首页</h1>
</body>
</html>
cookie很容易被恶意的客户端伪造。假如想在cookie中保存当前登录用户的id之类的信息,你需要对cookie做签名以防止伪造。Tornado通过set_secure_cookie和get_secure_cookie方法直接支持了这种功能。要使用这些方法,需要在创建应用时提供一个密钥,名字为cookie_secret。可以把它作为一个关键词参数传入应用的设置中:



6. 原生Ajax

1)概述

Ajax主要就是使用XMLHttpRequest对象来完成请求的操作,该对象在主流浏览器中均存在。


2)XMLHttpRequest对象的主要方法

a. void open(String method, String url, Boolean async)

用于创建请求

参数:

method: 请求方式(字符串类型),如:POST、GET、DELETE

url:要请求的地址(字符串类型)

async:是否异步(布尔类型)


b. void send(String body)

用于发送请求

参数:

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


c. void setRequestHeader(String header, String value)

用于设置请求头

参数:

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

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


d. String getAllResponseHeaders()

获取所有响应头

返回值:响应头数据(字符串类型)


e. String getResponseHeader(String header)

获取响应头中指定header的值

参数:

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

返回值:

响应头中指定的header相应的值


f. void abort()

终止请求


3)XMLHttpRequest对象的主要属性

a. Number readyState 状态值(整数)

详情:

0:未初始化,尚未调用oepn()方法;

1:启动,调用了open()方法,为调用send()方法;

2:发送,已经调用了send()方法,未接收到响应;

3:接受,已经接受到部分相应数据;

4:完成,已经接受到全部相应数据;


b. Function onreadystatechange

当readyState的值改变时自动触发执行其对应的函数(回调函数)


c. String responseText

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


d. XmlDocument responseXML

服务器返回的数据(xml对象)


e. Number states

状态码(整数),如:200、404


f. String statesText

状态文本(字符串),如:OK、NotFound

3)发送Ajax请求:

xhr = new XMLHttpRequest()

xhr.onreadystatechange = func

xhr.open("GET", "url", true)

xhr.send()


4)案例代码

# -*-coding:utf-8-*-

import tornado.ioloop
import tornado.web
import time

INPUTS_LIST = []

class IndexHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render("index.html", )

class ManageHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        cookie = self.get_cookie("auth")
        if cookie == "1":
            self.render("/manage.html")
        else:
            self.redirect("/login")


class LoginHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render("login.html", status_text="")

    def post(self, *args, **kwargs):
        dic = {"status": True, "message": ""}
        user = self.get_argument("username", None)
        pwd = self.get_argument("password", None)

        if user == "Steve" and pwd == "123":
            pass
        else:
            dic["status"] = False
            dic["message"] = "用户名或密码错误"
        import json
        self.write(json.dumps(dic))

class LogoutHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.set_cookie("auth", "0", expires=time.time())
        self.redirect("/login")

settings = {
    "template_path": "views", # 模板路径的配置
    "cookie_secret": "vjovuanefaelut",
    "static_path": "statics"
}

# 路由系统,或路由映射
application = tornado.web.Application([
    (r"/index", IndexHandler),
    (r"/login", LoginHandler),
    (r"/manage", ManageHandler),
    (r"/logout", LogoutHandler)
], **settings)

if __name__ == '__main__':
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input id="user" type="text" name="username">
    <input id="pwd" type="password" name="password">
    <button onclick="SubmitForm();">登录</button>

    <script type="text/javascript">
        xhr = null;
        function SubmitForm(){
            xhr = new 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("user").value
                + ";password=" + document.getElementById("pwd").value);
        }

        function func(){
            if(xhr.readyState == 4){
                console.log(xhr.responseText);
                var data = xhr.responseText;
                var ret_dic = JSON.parse(data);
                if(ret_dic.status){

                }else{
                    alter(ret_dict.message);
                }
            }
        }
    </script>

</body>
</html>

5)利用jQuery使用ajax的代码案例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input id="user" type="text" name="username">
    <input id="pwd" type="password" name="password">
    <button onclick="SubmitForm();">登录</button>

    <script src="{{ static_url(jquery-3.2.1.js) }}">
        username = $("#user").value();
        password = $("#pwd").value();
        
        function SubmitForm(){
            $.post("/login", {"username": username, "password": password}, function(callback){
                console.log(callback);
            });
        }
    </script>

</body>
</html>



7. Tornado的路由系统

1)基本路由

# 路由映射,基本路由
application = tornado.web.Application([
    (r"/index", home.IndexHandler)
], **settings)

2)基础正则路由

# 路由映射,基于正则的动态路由
application = tornado.web.Application([
    (r"/index/(?P<num>\d*)(?P<nid>\d*)", home.IndexHandler)
], **settings)

Tornado自定义分页代码案例:

# -*-coding:utf-8-*-
import tornado.web
import tornado.ioloop
import home

settings = {
    "template_path": "views", # 模板路径的配置
    "static_path": "statics", # 静态文件
}

# 路由映射,基于正则的动态路由
application = tornado.web.Application([
    (r"/index/(?P<page>\d*)", home.IndexHandler)
], **settings)

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
# -*-coding:utf-8-*-
import tornado.web

LIST_INFO = [
    {"username":"Steve", "email":"[email protected]"},
]

for i in range(99):
    temp = {"username": "John" + str(i), "email": str(i) + "[email protected]"}
    LIST_INFO.append(temp)

# 每页显示5条数据,page是当前页
# 第一页:0:5
# LIST_INFO[0:5]
# 第二页:5:10
# LIST_INFO[5:10]
# start: (page - 1) * 5
# end: page * 5

class PageDivision:
    def __init__(self, current_page, all_item):
        try:
            current_page = int(current_page)
        except:
            current_page = 1

        if current_page < 1:
            current_page = 1
        self.current_page = current_page


        all_pager, c = divmod(all_item, 5)
        if c > 0:
            all_pager += 1
        self.all_pager = all_pager



    @property
    def start(self):
        return (self.current_page - 1) * 5

    @property
    def end(self):
        return self.current_page * 5

    def page_str(self, base_url):
        list_page = []
        # former, later; former代表当前页的前5页,later代表当前页的后5页
        # range(当前页 - 5, 当前页 + 5 + 1)
        """
        当总页数 < 11时,显示1-总页数
        当总页数 > 11时:
            如果当前页 <= 6:
                1 - 11页
            如果当前页 > 6:
                如果: 当前页 + 5 > 总页数:
                    总页数 - 11, 总页数
                else:
                    当前页 - 5, 当前页 + 5
        """
        if self.all_pager < 11:
            former = 1
            later = self.all_pager
        else:
            if self.current_page <= 6:
                former = 1
                later = 11
            else:
                if (self.current_page + 5) > self.all_pager:
                    former = self.all_pager - 10
                    later = self.all_pager
                else:
                    former = self.current_page - 5
                    later = self.current_page + 5

        # 首页
        first_page = "<a href='%s1'>首页</a>" % (base_url,)
        list_page.append(first_page)

        # 上一页 current_page - 1
        if self.current_page == 1:
            prev_page = "<a href='javascript:void(0);'>上一页</a>"
        else:
            prev_page = "<a href='%s%s'>上一页</a>" % (base_url, self.current_page - 1)
        list_page.append(prev_page)

        for p in range(former, later + 1):
            if p == self.current_page:
                tmp = "<a class='active' href='%s%s'>%s</a>" % (base_url, p, p)
            else:
                tmp = "<a href='%s%s'>%s</a>" % (base_url, p, p)
            list_page.append(tmp)

        # 下一页 current_page + 1
        if self.current_page == self.all_pager:
            next_page = "<a href='javascript:void(0);'>下一页</a>"
        else:
            next_page = "<a href='%s%s'>下一页</a>" % (base_url, self.current_page + 1)
        list_page.append(next_page)

        # 尾页
        last_page = "<a href='%s%s'>尾页</a>" % (base_url, self.all_pager)
        list_page.append(last_page)

        # 页面跳转
        jump = """<input type='text'/><a onclick="Jump('%s', this);">跳转</a>""" % ('/index/',)
        script = """<script>
            function Jump(baseUrl, arg){
                var val = arg.previousElementSibling.value;
                if(val.trim().length > 0){
                    location.href = baseUrl + val;
                }
            }
        </script>"""
        list_page.append(jump)
        list_page.append(script)

        str_page = "".join(list_page)
        return str_page

class IndexHandler(tornado.web.RequestHandler):
    def get(self, page):

        page_obj = PageDivision(page, len(LIST_INFO))

        current_list = LIST_INFO[page_obj.start:page_obj.end]

        str_page = page_obj.page_str("/index/")

        self.render("home/index.html", list_info=current_list, current_page=page_obj.current_page, str_page=str_page)

    def post(self, page):
        user = self.get_argument("username")
        email = self.get_argument("email")
        temp = {"username":user, "email":email}
        LIST_INFO.append(temp)
        self.redirect("/index/" + page)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .pager a{
            display:inline-block;
            padding:5px;
            margin:2px;
            background-color:cornflowerblue;
        }
        .pager a.active{
            background-color:red;
            color:white;
        }
    </style>
</head>
<body>
    <h1>提交数据</h1>
    <form method="post" action="/index/{{current_page}}">
        <input name="username" type="text">
        <input name="email" type="text">
        <input type="submit" value="提交">
    </form>
    <h1>显示数据</h1>

    <table border="1">
        <thead>
            <tr>
                <th>用户名</th>
                <th>邮箱</th>
            </tr>
        </thead>
        <tbody>
            {% for line in list_info %}
                <tr>
                    <td>{{line["username"]}}</td>
                    <td>{{line["email"]}}</td>
                </tr>
            {% end %}
        </tbody>
    </table>
    <div class="pager">
        {% raw str_page %}
    </div>
</body>
</html>

4)二级域名路由

application = tornado.web.Application("123.abc.com$",[
    (r"/index/(?P<page>\d*)", 123.IndexHandler)
])


8. Tornado模板引擎

1)基本使用:

if else for


2)继承extends,页面整体布局

模板html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .pg-header{
            height: 48px;
            background-color:black;
        }
        .pg-footer{
            height:100px;
            background-color: #ddd;
        }
    </style>
    {% block css %}{% end %}
</head>
<body>
    <div class="pg-header"></div>

    <div class="pg-content">
        {% block body %}{% end %}
    </div>

    <div class="pg-footer">Footer</div>
    <script src="aaa"></script>
    {% block js %}{% end %}
</body>
</html>
Handler代码:

# -*-coding:utf-8-*-
import tornado.web
from core import Handler

class IndexHandler(Handler.MyHandler):
    def get(self, *args, **kwargs):
        self.render("extend/index.html", list_info=[1,2,3,4,5])

    def post(self, *args, **kwargs):
        pass

class Index2Handler(Handler.MyHandler):
    def get(self, *args, **kwargs):
        self.render("extend/index2.html", list_info=[11,22,33,44,55])
继承的页面:

{% extends '../master/layout.html'%}

{% block body %}
    <h1>Index</h1>
    {% include "../include/form.html"%}
{% end %}

{% block js %}
    <script>
        console.log("SSS");
    </script>
{% end %}
主程序代码:

# -*-coding:utf-8-*-
import tornado.web
import tornado.ioloop
import home
import extend

settings = {
    "template_path": "views", # 模板路径的配置
    "static_path": "statics", # 静态文件
}

# 路由映射,基于正则的动态路由
application = tornado.web.Application([
    (r"/index/(?P<page>\d*)", home.IndexHandler),
    (r"/extend", extend.IndexHandler)
], **settings)

# application = tornado.web.Application("123.abc.com$",[
#     (r"/index/(?P<page>\d*)", 123.IndexHandler)
# ])


if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()


3)导入include,组件

常用的模块:

<form action="/">
    <input type="text">
    <input type="submit">
</form>
<ul>
    {% for item in list_info %}
        <li>{{item}}</li>
    {% end %}
</ul>
引用常用模块的页面

{% extends '../master/layout.html'%}

{% block body %}
    <h1>Index</h1>
    {% include "../include/form.html"%}
{% end %}

{% block js %}
    <script>
        console.log("SSS");
    </script>
{% end %}




9. Cookie详述

本质:浏览器端存放的键值对

特性:每次http请求都会携带

self.cookies 获取所有cookie

self.get_cookie("k1") 获取指定cookie

self.set_cookie("k1", "1") 设置cookie


1)在浏览器上使用javascript

document.cookie['k1']

document.cookie="k2=2" 新增cookie

document.cookie="k3=3;path=/;" 新增cookie并指定在某路径下使用


2)案例

// 设置cookie,指定秒数过期,超时时间是看不到的
        function setCookie(name, value, expires){
            var current_date = new Date(); //获取当前时间
            current_date.setSeconds(current_date.getSeconds() + 5); //当前时间+5秒
            document.cookie = name + "=" + value + ";expires=" + current_date.toUTCString();
        }
参数:

  • domain 指定域名下的cookie
  • path 域名下制定url中的cookie
  • secure https使用

注:jQuery中也有指定的插件jQuery Cookie专门用于操作cookie:$.cookie("k1","1", {"path":"", "domain":"", expires=7});


3)基于cookie作用户验证

# -*-coding:utf-8-*-
import tornado.ioloop
import tornado.web

class IndexHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        if self.get_argument("user", None) in ["Steve", "John"]:
            # self.set_cookie("name", self.get_argument("user", None))
            self.set_secure_cookie("name", self.get_argument("user"))
        else:
            self.write("请登录")

class ManagerHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        if str(self.get_secure_cookie("name", None), encoding='utf-8') in ["Steve", "John"]:
            self.write("欢迎登陆:" + str(self.get_secure_cookie("name", None), encoding='utf-8'))
        else:
            self.redirect("/index")


settings = {
    "template_path": "views",
    "static_path": "statics",
    "cookie_secret": "dfjaouenbx",
}

application = tornado.web.Application([
    (r"/index", IndexHandler),
    (r"/manager", ManagerHandler),
], **settings)

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()




10. Session

自定义Session代码案例

# -*-coding:utf-8-*-
import tornado.ioloop
import tornado.web
container = {}

class Session:
    def __init__(self, handler):
        self.handler = handler
        self.random_str = None

    def __generate_random_str(self):
        import hashlib
        import time
        obj = hashlib.md5()
        obj.update(bytes(str(time.time())))
        random_str = obj.hexdigest()

        return random_str

    def __setitem__(self, key, value):
        # 在container中加入随机字符串
        # 定义专属于自己的数据
        # 在客户端中写入随机字符串
        # 判断,请求的用户是否已有随机字符串
        if not self.random_str:
            random_str = self.handler.get_cookie("ran_str")
            if not random_str:
                random_str = self.__generate_random_str()
                container[random_str] = {}
            else:
                # 客户端有随机字符串

                # 如果服务端有对应的随机字符串
                if random_str in container.keys():
                    pass
                # 若没有则生成一个新的,并更新客户端的随机字符串
                else:
                    random_str = self.__generate_random_str()
                    container[random_str] = {}

            self.random_str = random_str

        container[self.random_str][key] = value
        for k,v in container.items():
            print k, ":", v


        self.handler.set_cookie("ran_str", self.random_str)

    def __getitem__(self, key):
        # 获取客户端的随机字符串
        # 从container中获取专属于我的数据
        # 专属信息[key]
        random_str = self.handler.get_cookie("ran_str")
        # 如果客户端没有则返回None
        if not random_str:
            return None

        user_info_dict = container.get(random_str, None)
        # 如果客户端没有随机字符串,返回None
        if not user_info_dict:
            return None

        value = user_info_dict.get(key, None)
        return value


class BaseHandler(tornado.web.RequestHandler):
    def initialize(self):
        self.session = Session(self)

class IndexHandler(BaseHandler):
    def get(self, *args, **kwargs):
        if self.get_argument("user", None) in ["Steve", "John"]:
            self.session["is_login"] = True
            self.session["username"] = self.get_argument("user", None)
        else:
            self.write("请登录")

class ManagerHandler(BaseHandler):
    def get(self, *args, **kwargs):
        login = self.session["is_login"]

        if login:
            self.write(self.session["username"])
        else:
            self.write("失败")

settings = {
    "template_path": "views",
    "static_path": "statics",
    "cookie_secret": "dfjaouenbx",
}

application = tornado.web.Application([
    (r"/index", IndexHandler),
    (r"/manager", ManagerHandler),
], **settings)

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()


11. Ajax

AJAX,Asynchronous JavaScript and XML(异步的JavaScript和XML),一种创建交互式网页应用的网页开发技术方案。

1)Ajax的使用场景:

注册时,输入用户名自动检测用户是否已经存在

登录时,提示用户名密码错误

删除数据行时,将行ID发送到后台,后台在数据库中删除,数据库删除成功后,在页面DOM中将数据行也删除


2)伪Ajax,由于HTML标签的iframe标签具有局部加载内容的特性,所以可以用来伪造Ajax请求

<!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>
        <h2>加载页面位置</h2>
        <iframe id="iframePosition" style="width:100%;height:500px;"></iframe>
    </div>

    <script type="text/javascript">
        window.onload = function(){
            var currentDate = new Date();
            document.getElementById("currentTime").innerText = currentDate.getTime()
        }

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

3)原生Ajax

Ajax就是使用XmlHttpRequest对象来完成请求的操作

代码案例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>XMLHttpRequest - Ajax请求</h1>
    <input type="button" onclick="XhrGetRequest();" value="Get发送请求">
    <input type="button" onclick="XhrPostRequest();" value="Post发送请求">

    <script src="jquery-3.2.1.js"></script>
    <script>
        function GetXHR(){
            var xhr = null;
            if(XMLHttpRequest){
                xhr = new XMLHttpRequest();
            }else{
                xhr = new ActiveXObject("Microsoft.XMLHTTP");
            }
            return xhr;
        }

        function XhrPostRequest(){
            var xhr = GetXHR();
            // 定义回调函数
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4){
                    // 已经接受到全部响应数据,执行以下操作
                    var data = xhr.responseText;
                    console.log(data)
                }
            };
            // 指定连接方式和地址--文件方式
            xhr.open("POST", "/index", true);
            // 设置请求头
            xhr.setRequestHeader("Content-Type", "content-type","application/x-www-form-urlencoded; charset=UTF-8")
            // 发送请求
            xhr.send("n1=1; n2=2")
        }

        function XhrGetRequest(){
            var xhr = GetXHR();
            // 定义回调函数
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4){
                    // 已经接受到全部响应数据,执行以下操作
                    var data = xhr.responseText;
                    console.log(data)
                }
            };
            // 指定连接方式和地址--文件方式
            xhr.open("get", "/index", true);
            // 发送请求
            xhr.send();
        }
    </script>
</body>
</html>


4)jQuery发送Ajax请求

(1)导入jquery

(2)案例代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>XMLHttpRequest - Ajax请求</h1>
    <input type="button" onclick="XhrGetRequest();" value="Get发送请求">
    <input type="button" onclick="XhrPostRequest();" value="Post发送请求">

    <script src="jquery-3.2.1.js"></script>
    <script>
        function GetXHR(){
            var xhr = null;
            if(XMLHttpRequest){
                xhr = new XMLHttpRequest();
            }else{
                xhr = new ActiveXObject("Microsoft.XMLHTTP");
            }
            return xhr;
        }

        function XhrPostRequest(){
            var xhr = GetXHR();
            // 定义回调函数
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4){
                    // 已经接受到全部响应数据,执行以下操作
                    var data = xhr.responseText;
                    console.log(data)
                }
            };
            // 指定连接方式和地址--文件方式
            xhr.open("POST", "/index", true);
            // 设置请求头
            xhr.setRequestHeader("Content-Type", "content-type","application/x-www-form-urlencoded; charset=UTF-8")
            // 发送请求
            xhr.send("n1=1; n2=2")
        }

        function XhrGetRequest(){
            var xhr = GetXHR();
            // 定义回调函数
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4){
                    // 已经接受到全部响应数据,执行以下操作
                    var data = xhr.responseText;
                    console.log(data)
                }
            };
            // 指定连接方式和地址--文件方式
            xhr.open("get", "/index", true);
            // 发送请求
            xhr.send();
        }


        $.get({
            url: "地址",
            data: {"k1":"v1",},
            dataType: "xml", // 数据类型:text,json,xml,script,jsonp
            success:function(da){
                // obj = JSON.parse(da);
                eval("<script>alert(123);</")
            }
        });


        $.post({
            url: "地址",
            data: {"k1":"v1",},
            dataType: "xml", // 数据类型:text,json,xml,script,jsonp
            success:function(da){
                // obj = JSON.parse(da);
                eval("<script>alert(123);</")
            }
        });

        $.getJSON({
            url: "地址",
            data: {"k1":"v1",},
            success:function(da){

            }
        })

        $.getScript({
            url: "地址",
            data: {"k1":"v1",},
            success:function(da){

            }
        })

    </script>
</body>
</html>



(3)部分参数:

url: 请求地址

type:请求方式,GET、POST

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将自动替换?为正确的函数名,以执行回调函数



(4)代码案例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <p><input type="button" onclick="XmlSendRequest();" value="Ajax请求"></p>

    <script src="jquery-3.2.1.js"></script>
    <script>
        function XmlSendRequest(){
            $.ajax({
                url: "http://10.10.1.1:8888/test",
                type: "POST",
                data: {"k1":"v1",},
                dataType: "text",
                success: function(data){
                    console.log(data)
                }
            })
        }
    </script>
</body>
</html>


5)跨域Ajax

对于Ajax请求,如果跨域名请求,默认[浏览器]不允许

由于浏览器的同源策略,跨域Ajax不允许进行

Ajax允许有src的标签跨域,例如允许script、img、iframe块

(1)基于JSONP实现跨域Ajax

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="button" value="Ajax" onclick="DoAjax();">
    <input type="button" value="JSONPAjax" onclick="DoJSONPAjax();">

    <script src="jquery-3.2.1.js"></script>
    <script>
        function DaAjax(){
            $.ajax({
                url: "/index",
                type: "post",
                data: {"k1": "v1"},
                success: function(arg){
                    console.log(arg);
                }
            })
        }

        function DoJSONPAjax(){
            // var tag = document.createElement("script");
            // tag.src = "http://www.xiaohua.com";
            // document.head.appendChild(tag);
            // document.head.removeChild(tag);
            
            $.ajax({
                url: "",
                dataType: "jsonp",
                jsonpCallBack: "func"
            })
        }
    </script>
</body>
</html>

(2)基于CORS实现跨域Ajax

本质:客户端不变,服务器端设置响应头即可

简单请求或非简单请求

简单请求:一次请求

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

条件:

a. 请求方式:head、get、post

b. 请求头信息:

 Accept

Accept-Language

Content-Language

Last-Event_ID

Content-Type 对应的值是以下三个中的任意一个

 application/x-www-form-urlencoded

  multipart/form-data

 text/plain

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

代码案例:

import tornado.ioloop
import tornado.web

class IndexHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        pass

    def post(self, *args, **kwargs):
        pass

class CorsHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.write('{"status": 1, "message": "get"}')

    def post(self, *args, **kwargs):
        self.set_header("Access-Control-Allow-Origin", "http://")
        self.write('{"status": 1, "message": "get"}')

    def options(self, *args, **kwargs):
        print("options")
        # 允许put方法来
        self.set_header("Access-Control-Allow-Methods", "*")
        self.set_header("Access-Control-Allow-Methods", "PUT")
        self.set_header("Access-Control-Allow-Origin", "http://www.xxx.com")
        self.set_header("Access-Control-Allow-Headers", "k1, k2")
        self.set_header("Access-Control-Max-Age", 10)

    def put(self, *args, **kwargs):
        self.set_header("Access-Control-Allow-Methods", "*")
        print("put")

settings = {
    "template_path": "views",
    "static_path": "statics",
    "static_url_prefix": "/statics/",
}

application = tornado.web.Application([
    (r"/index", IndexHandler),
    (r"/cors", CorsHandler),
], **settings)


6)跨域传输cookie

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

浏览器端:XMLHttpRequest的withCredentials为true

服务器端:Access-Control-Allow-Credentials为true

注意:服务器端相应的Access-Control-Allow-Origin不能是通配符*



12. 文件上传

1)Form表单上传

<!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="text" name="user">
        <input type="checkbox" name="hobby" value="1">喜剧;
        <input type="checkbox" name="hobby" value="2">动作;
        <input type="checkbox" name="hobby" value="3">剧情;
        <input type="file" name="fff">
        <input type="submit" value="提交">
    </form>
    <ul>
        {% for item in img_list %}
        <li><img style="height:80px;width:50px;" src="/img/{{item}}"></li>
        {% end %}
    </ul>
</body>
</html>

# -*-coding:utf-8-*-
import tornado.ioloop
import tornado.web

IMG_LIST = []

class IndexHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.render("index2.html", img_list=IMG_LIST)

    def post(self, *args, **kwargs):
        print self.get_argument("user")
        print self.get_arguments("hobby")

        file_metas = self.request.files.get("fff", None)
        if not file_metas:
            self.write("Invalid file")
            self.redirect("index2.html")

        for meta in file_metas:
            file_name = meta["filename"]
            import os
            path = os.path.join("img", file_name)

            with open(path, "wb") as f:
                f.write(meta["body"])

            IMG_LIST.append(file_name)

settings = {
    "template_path": "views",
    "static_path": "statics",
    "static_url_prefix": "/statics/"
}

application = tornado.web.Application([
    (r"/index", IndexHandler),
    (r"/index2", IndexHandler)
], **settings)

if __name__=="__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()


2)Ajax上传

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input type="file" id="img">
    <input type="button" value="提交" onclick="UploadFile();">

    <script src="jquery-3.2.1.js"></script>
    <script>
        // 通过ajax方式上传文件
        function UploadFile(){
            // 获取文件对象
            var fileObj = document.getElementById("img").files[0];

            // 创建form对象
            var form = new FormData();
            form.append("image", fileObj)

            var xhr = new XMLHttpRequest();
            xhr.open("post", "/index3", true);
            xhr.send(form);
        }

        // 通过jquery方式上传文件
        function UploadFile2(){
            var fileObj = $("#img").files[0];
            var form = new FormData();
            form.append("image", fileObj);

            $.ajax({
                type: "POST",
                url: "/index3",
                data: form,
                processData: false, // 必须加上这个参数
                contentType: false, // 必须加上这个参数
                success: function(arg){
                    console.log(arg);
                }
            })
        }
    </script>
</body>
</html>


3)iframe,解决ie等浏览器兼容性问题

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form id="my_form" name="form" action="/index4" method="post" enctype="multipart/form-data">
        <div id="main">
            <input name="file1" id="my_file" type="file">
            <input type="button" name="action" value="Upload" onclick="Redirect();">
            <iframe id="my_iframe" name="my_iframe" src="" class="hide"></iframe>
        </div>
    </form>

    <script src="jquery-3.2.1.js"></script>
    <script>
        function Redirect(){
            document.getElementById("my_iframe").onload = Test;
            // 指定提交到iframe
            document.getElementById("my_form").target = "my_iframe";
            document.getElementById("my_form").submit();
        }

        function Test(arg){
            var t = $("my_iframe").contents().find("body").text();
            console.log(t);
    </script>
</body>
</html>



13. 自定义Form表单验证

# -*-coding:utf-8-*-
import tornado.ioloop
import tornado.web
import re

class IPField:
    REGULAR = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d\d)){3}$"

    def __init__(self, error_dict=None, required=True):
        # 封装了错误信息
        self.error_dict = {}
        if error_dict:
            self.error_dict.update(error_dict)
        self.required = required

        self.error = None
        self.is_valid = False
        self.value = None

    def validate(self, name, input_value):
        """
        :param name: 字段名
        :param input_value: 用户表单中输入的内容
        :return:
        """
        if not self.required:
            # 用户输入可以为空
            self.is_valid = True
            self.value = input_value
        else:
            if not input_value.strip():
                if self.error_dict.get("required", None):
                    self.error = self.error_dict["required"]
                else:
                    self.error = "%s is required" % name
            else:
                ret = re.match(IPField.REGULAR, input_value)
                if ret:
                    self.id_valid = True
                    self.value = ret.group()
                else:
                    if self.error_dict.get("valid", None):
                        self.error = self.error_dict["valid"]
                    else:
                        self.error = "%s is invalid" % name

class FileField:
    REGULAR = "^(\w+\.pdf)|(\w+\.mp3)|(\w+\.py)$"

    def __init__(self, error_dict=None, required=True):
        # 封装了错误信息
        self.error_dict = {}
        if error_dict:
            self.error_dict.update(error_dict)
        self.required = required

        self.error = None
        self.is_valid = True
        self.value = []

    def validate(self, name, all_file_name_list):
        """
        :param name: 字段名
        :param input_value: 所有文件文件名
        :return:
        """
        self.name = name
        if not self.required:
            # 用户输入可以为空
            self.is_valid = True
            self.success_file_name_list = all_file_name_list
        else:
            if not all_file_name_list:
                self.is_valid = False
                if self.error_dict.get("required", None):
                    self.error = self.error_dict["required"]
                else:
                    self.error = "%s is required" % name
            else:
                # 循环所有的文件名
                for file_name in all_file_name_list:
                    ret = re.match(FileField.REGULAR, file_name)
                    if not ret:
                        self.is_valid = False
                        if self.error_dict.get("valid", None):
                            self.error = self.error["valid"]
                        else:
                            self.error = "%s is invalid" % name
                        break
                    else:
                        self.success_file_name_list.append(file_name)

    def save(self, request, path="statics/upload"):
        # 所有文件列表
        file_metas = request.files.get(self.name)
        # 循环文件列表
        temp_list = []
        for meta in file_metas:
            # 每个文件的文件名
            file_name = meta["filename"]
            import os
            new_file_name = os.path.join(path, file_name)
            if file_name and file_name in self.value:
                temp_list.append(new_file_name)
                with open(new_file_name, "wb") as f:
                    f.write(meta["body"])
        self.value = temp_list

class BaseForm(object):
    def check_valid(self, request):
        # 获取用户提交的数据
        # 将用户提交的数据和正则表达式匹配
        flag = True
        value_dict = {}
        error_message_dict = {}
        success_value_dict = {}
        form_dict = self.__dict__
        for key, regular in form_dict.items():
            # key: ip...
            # request: HomeIndex对象,self.get... self
            # regular: IPFiled(required=True)
            # input_value为用户输入的值
            if type(regular) == FileField:
                # 获取文件名
                file_list = request.request.files.get(key)
                # [{"body":"xx:, "filename":"x"}, {}]
                input_value = []
                for item in file_list:
                    input_value.append(item["filename"])
                # 所有文件名进行验证

            else:
                input_value = request.get_argument(key)

                # regular为IPField的对象
                # 将具体的验证,放在IPField对象中
                regular.validate(key, input_value)
                if regular.is_valid:
                    success_value_dict[key] = regular.value
                else:
                    error_message_dict[key] = regular.error
                    flag = False

                value_dict[key] = input_value


        return flag, success_value_dict, error_message_dict

class MainForm(BaseForm):
    def __init__(self):
        self.host = "(.*)"
        self.ip = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d\d)){3}$"
        self.port = "(\d+)"
        self.phone = "^1[3|4|5|8][0-9]\d{8}$"

class HomeForm(BaseForm):
    def __init__(self):
        self.host = "(.*)"
        self.ip = IPField(required=True)


class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("index.html")

    def post(self, *args, **kwargs):
        obj = MainForm()
        is_valid, value_dict = obj.check_valid(self)
        # 如果全部验证成功,将用户输入的所有数据放在obj.value_dict
        if is_valid:
            print value_dict

class HomeHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("index.html", error_dict=None)

    def post(self, *args, **kwargs):
        files = self.request.files.get("f",[])
        # files=[文件1,文件2]

        obj = HomeForm()
        is_valid, success_dict, error_dict = obj.check_valid(self)
        # 如果全部验证成功,将用户输入的所有数据放在obj.value_dict
        if is_valid:
            print "success", success_dict
        else:
            print "error", error_dict
            self.render("home.html", error_dict=error_dict)

settings = {
    "template_path": "views",
    "static_path": "statics",
    "static_url_prefix": "/statics/"
}

application = tornado.web.Application([
    (r"/index", IndexHandler),
    (r"/home", HomeHandler),
], **settings)

if __name__ == "__main__":
    application.listen(9999)
    tornado.ioloop.IOLoop.instance().start()

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/home" method="post">
        <input type="text" name="ip" placeholder="ip">

        <div>
            <input type="file" name="f">
            <input type="file" name="f">
        </div>

        {% if error_dict %}
        <span>{{error_dict['ip']}}</span>
        {% end %}
        <input type="submit" value="submit">
    </form>
</body>
</html>




猜你喜欢

转载自blog.csdn.net/wayne12081213/article/details/79232962