python笔记(tornado初识)

一、tornado介绍

  1. 轻量级web框架

  2. 异步非阻塞IO处理方式

  3. 出色的抗负载能力

  4. 优异的处理性能,不依赖多进程/多线程,一定程度上解决GIL问题

  5. WSGI全栈替代产品,推荐同时使用其web框架和HTTP服务器

  6. socket连接建立过程

    在这里插入图片描述

  7. tornado生命周期
    在这里插入图片描述

二、初步简单使用

  1. httpserver形式

    from tornado import web  # tornado的基础web框架模块
    from tornado import ioloop  # tornado的核心IO循环模块,封装了linux的epoll和BSD的kqueue
    from tornado import httpserver
    
    
    # 相当于Django中的视图
    # 一个业务处理类
    class IndexHandler(web.RequestHandler):
        """处理get请求"""
        def get(self, *args, **kwargs):
            self.write("hello world")  # 做出响应数据
    
    
    if __name__ == '__main__':
    
        app = web.Application([
            (r"/", IndexHandler)
        ])
        # 实例化http服务对象
        http_server = httpserver.HTTPServer(app)
        # 绑定IP和端口
        http_server.listen(8000)
        ioloop.IOLoop.current().start()
    
  2. 多进程形式

    from tornado import web  # tornado的基础web框架模块
    from tornado import ioloop  # tornado的核心IO循环模块,封装了linux的epoll和BSD的kqueue
    from tornado import httpserver
    
    
    # 相当于Django中的视图
    # 一个业务处理类
    class IndexHandler(web.RequestHandler):
        """处理get请求"""
        def get(self, *args, **kwargs):
            self.write("hello world")  # 做出响应数据
    
    
    if __name__ == '__main__':
    
        app = web.Application([
            (r"/", IndexHandler)
        ])
        # 实例化http服务对象
        http_server = httpserver.HTTPServer(app)
        # 启动多个进程, 将服务器绑定到指定的端口
        http_server.bind(8000)
        http_server.start(5)  # 开启5个进程,不写默认启动一个进程
        """
        多进程存在问题:
            1.每个子进程都会从父进程中复制一份IOLoop实例,如果在创建子进程前修改了IOLoop,会影响所有子进程
            2.所有的进程都是由一个命令启动的,无法做到在不停止服务的情况下修改代码
            3.所有进程共享一个端口,想要分别监控很困难
        """
        ioloop.IOLoop.current().start()
    
  3. 常用形式

    import tornado.web  # tornado的基础web框架模块
    import tornado.ioloop  # tornado的核心IO循环模块,封装了linux的epoll和BSD的kqueue
    
    
    # 相当于Django中的视图
    # 一个业务处理类
    class IndexHandler(tornado.web.RequestHandler):
        """处理get请求"""
        def get(self, *args, **kwargs):
            self.write("hello world")  # 做出响应数据
    
    
    if __name__ == '__main__':
        """实例化一个app对象    Application:是tornado web框架的核心应用类,是与服务器对应的接口。
        里面保存了路由映射表,有一个listen方法用来创建一个http服务的实例,并绑定了端口"""
        app = tornado.web.Application([
            (r"/", IndexHandler)
        ])
        app.listen(8000)
        """
        IOLoop.current():返回当前线程IOLoop实例
        IOLoop.start():起动IOLoop实例的I/O循环,同时开启了监听
        """
        tornado.ioloop.IOLoop.current().start()
    
  4. 带options使用形式(三种使用方式)

    server_options.py

    from tornado import web  # tornado的基础web框架模块
    from tornado import ioloop  # tornado的核心IO循环模块,封装了linux的epoll和BSD的kqueue
    from tornado import httpserver
    from tornado import options
    import config
    
    """
    定义变量
    options.define(
        name="port",  # 变量名,唯一性
        default=8000,  # 设置默认值
        type=int,  # 数据类型
        help=None,  # 选项变量的提示信息
        metavar=None,  #
        multiple=False,  # 设置选项变量是否可以为多个值
        group=None,
        callback=None
    )
    options.options: 全局的options对象,所有定义的选项变量都会作为改对象的属性
    """
    
    # options.define(name="port", type=int, default=8000)  # (第一、二种方式)
    # options.define(name="list", type=str, default=[], multiple=True)  # (第一、二种方式)
    
    
    # 相当于Django中的视图
    # 一个业务处理类
    class IndexHandler(web.RequestHandler):
        """处理get请求"""
        def get(self, *args, **kwargs):
            self.write("hello world")  # 做出响应数据
    
    
    if __name__ == '__main__':
        # options.options.logging = None  # 关闭日志
        # options.parse_command_line()  # 转换命令行参数,并保存到options.options(第一种方式)
        # options.parse_config_file("config")  # 去配置文件中取参数(第二种方式.txt文档)
        # print(options.options.list)  #(第二种方式)
        print(config.options.get("list"))  # 直接去py文件中取,(第三种方式)
        app = web.Application([
            (r"/", IndexHandler)
        ])
        http_server = httpserver.HTTPServer(app)
        # 绑定IP和端口
        # http_server.listen(options.options.port)  # (第一种方式)
    
        # http_server.bind(options.options.port)  # 第二种方式
        # http_server.start(1) # 第二种方式
    
        http_server.bind(config.options.get("port"))  # 第三种方式
        http_server.start(1)  # 第三种方式
    
        ioloop.IOLoop.current().start()
    
    
    """
    第一种方式必须在终端启动:
        例如:python server_options.py --port=9000 --list=good,nice,cool
    """
    

    config.txt(方式二)

    port = 7000
    list = ["good", "nice", "well"]
    

    config.py(方式三)

    options = {
        "port": 7000,
        "list": ["good", "nice", "well"]
    }
    

三、异步IO

  1. 协程实现异步IO

    import time
    import threading
    
    
    # 版本一
    gen = None
    
    def longIo():
        def run():
            global gen
            print("开始延时操作。。。")
            time.sleep(5)
            try:
                gen.send("我发送的数据")
            except StopIteration as e:
                pass
            print("结束延时操作。。。")
        threading.Thread(target=run).start()
    
    
    def genCoroutine(func):
        def wrapper(*args, **kwargs):
            global gen
            gen = func(*args, **kwargs)
            next(gen)
        return wrapper
    
    
    @genCoroutine
    def reaA():
        print("开始执行reaA")
        res = yield longIo()
        print("接收的数据", res)
        print("结束执行reaA")
    
    
    def reaB():
        print("开始执行reaB")
        time.sleep(2)
        print("结束执行reaA")
    
    
    def main():
        reaA()
        reaB()
    
    main()
    
    
    # 版本二
    def longIo():
        print("开始延时操作。。。")
        time.sleep(5)
        print("结束延时操作。。。")
        yield "我发送的数据"
    
    
    def genCoroutine(func):
        def wrapper(*args, **kwargs):
            gen1 = func(*args, **kwargs)  # reqA生成器
            gen2 = next(gen1)  # longIo的生成器
    
            def run(g):
                res = next(g)  # 拿到longIo的返回数据
                try:
                    gen1.send(res)  # 返回给reqA数据
                except Exception as e:
                    pass
            threading.Thread(target=run, args=(gen2,)).start()
        return wrapper
    
    
    @genCoroutine
    def reaA():
        print("开始执行reaA")
        res = yield longIo()
        print("接收的数据", res)
        print("结束执行reaA")
    
    
    def reaB():
        print("开始执行reaB")
        time.sleep(2)
        print("结束执行reaA")
    
    
    def main():
        reaA()
        reaB()
    
    main()
    

    输出结果:

    开始执行reaA
    开始延时操作。。。
    开始执行reaB
    结束执行reaA
    结束延时操作。。。
    接收的数据 我发送的数据
    结束执行reaA

  2. 异步IO加回调函数

    import time
    import threading
    
    
    def longIo(callback):
        def run(cb):
            print("开始延时操作。。。")
            time.sleep(5)
            print("结束延时操作。。。")
            cb("回调函数接收值")
        threading.Thread(target=run, args=(callback, )).start()
    
    
    def finsh(data):
        """
        回调函数
        :param data:
        :return:
        """
        print("接收到longIo的值", data)
    
    
    def reaA():
        print("开始执行reaA")
        longIo(finsh)
        print("结束执行reaA")
    
    
    def reaB():
        print("开始执行reaB")
        longIo(finsh)
        print("结束执行reaB")
    
    
    def reaC():
        print("开始执行reaC")
        longIo(finsh)
        print("结束执行reaC")
    
    reaA()
    reaB()
    reaC()
    

    输出结果:

    开始执行reaA
    开始延时操作。。。
    结束执行reaA
    开始执行reaB
    开始延时操作。。。
    结束执行reaB
    开始执行reaC
    开始延时操作。。。
    结束执行reaC
    结束延时操作。。。
    结束延时操作。。。
    接收到longIo的值 回调函数接收值
    结束延时操作。。。
    接收到longIo的值 回调函数接收值
    接收到longIo的值 回调函数接收值

四、tornado基本框架结构

在这里插入图片描述

mysql_db.py

import pymysql

# 单例模式的装饰器
# def singleton(cls, *args, **kwargs):
#     instance = {}
#
#     def _singleton():
#         if cls not in instance:
#             instance[cls] = cls(*args, **kwargs)
#         return instance[cls]
#     return _singleton


class LongMySQl(object):
    def __init__(self, host, user, passwd, dbName):
        self.host = host
        self.user = user
        self.passwd = passwd
        self.dbName = dbName

    def connect(self):
        self.db = pymysql.connect(self.host, self.user, self.passwd, self.dbName)
        self.cursor = self.db.cursor(cursor=pymysql.cursors.DictCursor)

    def close(self):
        self.cursor.close()
        self.db.close()

    def get_one(self, sql):
        res = None
        try:
            self.connect()
            self.cursor.execute(sql)
            res = self.cursor.fetchone()
            self.close()
        except Exception:
            print("查询失败")
        return res

    def get_all(self, sql):
        res = ()
        try:
            self.connect()
            self.cursor.execute(sql)
            res = self.cursor.fetchall()
            self.close()
        except Exception:
            print("查询失败")
        return res
    #
    # def get_all_obj(self, sql, tableName, *args):
    #     resList = []
    #     filedsList = []
    #     if (len(args) > 0):
    #         for item in args:
    #             filedsList.append(item)
    #     else:
    #         filedsSql = "select COLUMN_NAME from information_schema.COLUMNS where table_name = '%s' and table_schema = '%s'"%(
    #             tableName,
    #             self.dbName
    #         )
    #         fields = self.get_all(filedsSql)
    #         for item in fields:
    #             filedsList.append(item[0])
    #
    #     res = self.get_all(sql)
    #     for item in res:
    #         obj = {}
    #         count = 0
    #         for x in item:
    #             obj[filedsList[count]] = x
    #             count += 1
    #         resList.append(obj)
    #     return resList

    def insert(self, sql):
        return self.__edit(sql)

    def update(self, sql):
        return self.__edit(sql)

    def delete(self, sql):
        return self.__edit(sql)

    def __edit(self, sql):
        count = 0
        try:
            self.connect()
            self.cursor.execute(sql)
            self.db.commit()
            count = self.cursor.lastrowid
            self.close()
        except Exception:
            print("事务提交失败")
            self.db.rollback()
        return count

orm.py(只有增加功能)

from application import Application


class ORM(Application):
    def save(self):
        tableName = (self.__class__.__name__).lower()
        fieldsStr = valuesStr = "("
        for field in self.__dict__:  # {'name': 1, 'gender': 2}
            fieldsStr += (field + ",")
            if isinstance(self.__dict__[field], str):
                valuesStr += ("'" + self.__dict__[field] + "',")
            else:
                valuesStr += (str(self.__dict__[field]) + ",")
        fieldsStr = fieldsStr[:len(fieldsStr) - 1] + ")"
        valuesStr = valuesStr[:len(valuesStr) - 1] + ")"
        sql = "insert into " + tableName + " " + fieldsStr + " values" + valuesStr
        self.db.insert(sql)

    def delete(self):
        pass

    def update(self):
        pass

    @classmethod
    def all(self):
        print("all")

    def filter(self):
        pass

models.py

from db.orm import ORM


class Student(ORM):
    def __init__(self, sname, gender):
        self.name = sname
        self.gender = gender

server.py

from tornado import ioloop  # tornado的核心IO循环模块,封装了linux的epoll和BSD的kqueue
from tornado import httpserver
import application
import config


if __name__ == '__main__':
    app = application.Application()
    http_server = httpserver.HTTPServer(app)
    http_server.bind(config.options.get("port"))  # 第三种方式
    http_server.start(1)  # 第三种方式
    ioloop.IOLoop.current().start()

config.py

import os


BASE_DIRS = os.path.dirname(__file__)

# 参数
options = {
    "port": 8000
}


# 数据库配置
mysql = {
    "host": "127.0.0.1",
    "user": "root",
    "passwd": "",
    "dbName": "db1"
}

# 配置

settings = {
    "static_path": os.path.join(BASE_DIRS, "static"),
    "template_path": os.path.join(BASE_DIRS, "templates"),
    # """
    # 加密cookie
    # """
    "cookie_secret": "G0SAhaKwQ6eU/lqXoW+b3g/at/cy1EKDluw/JcFlYtA=",
    # """
    # 设置tornado是否在调试模式下
    # 为Ture时:
    #     1.在Ture下可以自动重启(有改动时)
    #     2.取消缓存编译的模板(页面缓存)
    #     3.取消缓存静态文件的hash值(CSS样式改动的缓存)
    #     4.提供追踪信息()
    # """
    "debug": False,
    # """
    # 为Ture时:
    #     仅仅使用自动重启
    # """
    "autoreload": True,
    # """
    # 为Fale:
    #     仅仅取消缓存编译的模板
    # """
    "compiled_template_cache": False,
    # """
    # 为Fale:
    #     仅仅取消缓存静态文件的hash值
    # """
    "static_hash_cache": False,
    # """
    # 为True:
    #     仅仅提供追踪信息
    # """
    "serve_traceback": True,
    "xsrf_cookie": True,
    "login_url": "/login"
}

application.py

from db.mysql_db import LongMySQl
from tornado import web
from views import index
import config
import os


class Application(web.Application):
    def __init__(self):
        handlers = [
            # (r"/", index.IndexHandler),
            (r"/getone", index.GetOne, {"first": "1", "second": "2"}),  # 传参
            web.url(r"/parameter", index.ParameterHandler, name="parameter"),  # 反向解析
            (r"/ret_json", index.JsonHandler),  # json
            (r"/ret_json2", index.Json2Handler),  # json2
            (r"/header", index.HeaderHandler),  # 响应头
            (r"/status", index.StatusHandler),  # 状态码
            (r"/redirect", index.RedirectHandler),  # 重定向
            (r"/errostatus", index.ErrorHandler),  # 抛出错误
            (r"/re/(\w+)", index.ReHandler),  # 正则匹配
            # (r"/re/(?P<p1>\w+)", index.ReHandler),  # 正则匹配
            (r"/reget", index.ReGet),  # get请求获取参数
            (r"/studentdb", index.StudentsHandler),  # 数据库操作
            (r"/cookie", index.CookieHandler),  # cookie
            (r"/sccookie", index.ScCookieHandler),  # 加密cookie
            (r"/cookienum", index.CookieCountHandler),  # cookie记录访问次数
            (r"/xsrf", index.XsrfHandler),  # xsrf
            (r"/setxsrf", index.SetXsrfHandler),  # xsrf
            (r"/login", index.LoginHandler),  # xsrf
            (r"/auth", index.AuthHandler),  # auth认证
            # (r"/callbackasy", index.CallbackAsy),  # 回调函数异步请求
            (r"/genasy", index.GenvenletAsy),  # 回调函数异步请求
            (r"/genasy2", index.GenvenletAsy2),  # 回调函数异步请求
            (r"/chat", index.ChatHandler),  # websocket
            (r"/home", index.HomeHandler),  # 聊天home页面
            (r"/(.*)$", index.StaticFileHandler,
             {"path": os.path.join(config.BASE_DIRS, "static/html"), "default_filename": "main.html"}),  # 主页
        ]
        super(Application, self).__init__(handlers, **config.settings)  # 执行web.Application的__init__方法
        self.db = LongMySQl(
            config.mysql.get("host"),
            config.mysql.get("user"),
            config.mysql.get("passwd"),
            config.mysql.get("dbName"),
        )

index.py(*****)

from tornado.httpclient import AsyncHTTPClient
from tornado.web import RequestHandler
# from tornado.httputil import HTTPFile
from tornado.websocket import WebSocketHandler
from tornado import web
import config
import tornado
import json
import os


"""
视图:
    request对象:
        self.request.XXX
            request.method
            request.post
            request.uri
            request.host
            request.path
            request.query  # 请求参数部分
            request.version  # HTTP版本
            request.headers  # 字典类型
            request.body  # 请求体数据
            request.remote_ip  # 客户端ip
            request.files  # 用户上传的文件
    响应:
         self.write("xxxx")   # 可以写多个
         self.finish()  # 在finish下边就不要再写write
         
    请求方式:
         get, post, delete, put, patch, 
         head(类似于get请求,只不过响应中没有具体的内容,用户获取报头),
         options(返回URL支持的所有HTTP方法) 
         
    请求函数:
         initialize()
         prepare()  # 在请求方法之前执行 类似于中间件
         set_default_headers()
         write_error()
         on_finish()  # 在请求处理后执行,进行资源清理释放,或者日志处理
         执行顺序:
            在正常情况下:set_default_headers,initialize,prepare,(HTTP方法),on_finish
            在抛出错误时:set_default_headers,initialize,prepare,(HTTP方法),set_default_headers,write_error,on_finish
    
"""
"""
模板:
    1.配置模板路径
    2.渲染并返回:self.render(),self.render("index.html", num=100, dit=dict, **dict),
    3.语法:
        {{var}},{{a+b}}(可以是表达式)
        {{dit["name"]}}  #  字典取值
    4.条件语句:
        {% if xxx %}
        {% elif xxx %}
        {% else %}
        {% end %}
    5.循环:
        {" for i in arr "}
        {" end "}
    6.函数:
        static_url():
                <link rel="stylesheet" href="{{static_url('css/my_css.css')}}">
                优点: 创建了一个基于文件内容的hash值,并将其添加到URL末尾,这个hash值总能保证加载的总是最新版本
        自定义函数:
                def sum(a, b):
                    return a + b                
                self.render(),self.render("index.html", sum=sum)
                {{sum(10,20)}}
    7.转义
        
        raw:
            str = "<h1>nihao<h1\>"  
            {% raw str %}
        autoescape:
            {% autoescape None %}
            {{ str }}
        
        在配置中修改:
          setting中添加:
            "autoescape": None
            
        {{escape(str)}}开启转义
        
    8.继承:
        {% extends "base.html" %}
        {% block main %}
        
        {% end %}d
    9.静态文件
        static_path
        StaticFileHandler: 是tornado预制的用来提供静态资源的handler
        可以通过tornado.web.StaticFileHandler
            (r"/(.*)$", StaticFileHandler, {
            "path": os.path.join(config.BASE_DIRS, "static/html"),
            "default_filename": "index.html"
            }),  # 指定访问静态文件路径,和默认文件(写在所有路由下面)
    
    10.数据库:
        tornado未带ORM
        
安全cookie:
    setting中设置:"cookie_secret" = "xxxxxxxxxxx"   # 用base64与uuid生成
    ret = base64.b64encode(uuid.uuid4().bytes+uuid.uuid4().bytes)
    
异步:
    from tornado.httpclient import AsyncHTTPClient
    from tornado.httpclient import HTTPResponse, HTTPRequest

    因为epoll主要用来解决网络IO的并发问题,所以Tornado的异步也是主要体现在网络的IO异步上,即异步web请求
    AsyncHTTPClient: 提供异步web请求客户端,用来进行异步web请求
    fetch(request, callback = None)  用于执行一个web请求,并异步响应返回一个HTTPResponse
                                     request可以是一个url, 也可以是一个HTTPRequest对象   
"""


class IndexHandler(RequestHandler):
    """处理get请求"""
    def get(self, *args, **kwargs):
        url = self.reverse_url("parameter")
        self.write("hello world  <a href='{}'>到反向解析界面</a>".format(url))  # 做出响应数据


class GetOne(RequestHandler):
    """
    接收参数,写死的参数
    """
    def initialize(self, first, second):
        """
        接收参数(在执行get之前执行)
        :return:
        """
        self.first = first
        self.second = second

    def get(self, *args, **kwargs):
        print(self.first, self.second)
        self.write("hello world")


class ParameterHandler(RequestHandler):
    """
    获取用户传参,反向解析url
    """
    def get(self, *args, **kwargs):
        page = self.get_query_argument("page")  # 获取用户传参
        self.write("我是反向解析url过来的")


class JsonHandler(RequestHandler):
    """
    返回json数据
    """
    def get(self, *args, **kwargs):
        per = {
            "code": 1001,
            "msg": ""
        }
        per_json = json.dumps(per)
        self.set_header("Content-Type", "application/json;charset=UTF-8")  # 设置响应头
        """返回的Content-Type为text/html"""
        self.write(per_json)


class Json2Handler(RequestHandler):
    """
    返回json数据
    """
    def get(self, *args, **kwargs):
        per = {
            "code": 1001,
            "msg": ""
        }
        """write方法可以帮你转成json字符串,返回的Content-Type类型为application/json类型"""
        self.write(per)


class HeaderHandler(RequestHandler):
    """
    设置响应头
    """
    def set_default_headers(self):
        """
        在get之前调用
        :return:
        """
        self.set_header("Content-Type", "text/html;charset=UTF-8")


class StatusHandler(RequestHandler):
    """
    设置状态码
    """
    def get(self, *args, **kwargs):
        """若reason值为None,则状态码为正常值(如404,500....)"""
        self.set_status(status_code=1000, reason="描述状态码")
        self.write("xxxxxxx")


class RedirectHandler(RequestHandler):
    """
    重定向
    """
    def get(self, *args, **kwargs):
        self.redirect("/")


class ErrorHandler(RequestHandler):
    """
    抛出错误信息
    """
    def write_error(self, status_code, **kwargs):
        code = 200
        if status_code == 500:
            code = 500
            self.write("服务器内部出错")  # 返回500界面
        elif status_code == 404:
            code = 400
            self.write("资源不存在")  # 返回404界面
        self.set_status(code)

    def get(self, *args, **kwargs):
        flag = int(self.get_query_argument("flag"))
        if flag == 0:
            self.send_error(500)
        self.write("你是对的")


class ReHandler(RequestHandler):
    def get(self, p1, *args, **kwargs):
        print(p1)
        self.write("re...")


class ReGet(RequestHandler):
    """
    获取请求数据:
    无论get请求还是post请求都可以用:
        self.get_argument()s
        self.get_argument()
    """
    def get(self, *args, **kwargs):
        """
        获取get请求数据
         self.get_query_argument(name="", default=ARG_DEFAULT, strip=True)
        如果出现同名参数,则返回最后一个值的结果;default:若没有name则返回默认值
        若default也未设置,则报错
        strip:去除左右空格
        """
        # page_list = self.get_query_arguments()  # 这样拿到的是一个传参列表
        # page = self.get_query_argument("page")  # 拿到单个数据
        self.render("login.html")



    def post(self, *args, **kwargs):
        """
        获取post请求数据
        self.get_body_argument(name="", default=ARG_DEFAULT, strip=True)
        :param args:
        :param kwargs:
        :return:
        """
        username = self.get_body_argument("username", strip=True)
        password = self.get_body_argument("password", strip=True)
        """
        file数据格式:
        {
        'img': [
            {'filename': 'mmexport1526622518750.jpg', 'body':b"xxxxxx", 'content_type': 'image/jpeg'}
            {'filename': 'mmexport1526622518750.jpg', 'body':b"xxxxxx", 'content_type': 'image/jpeg'}
            ]
        "file":[
            {'filename': 'mmexport1526622518750.jpg', 'body':b"xxxxxx", 'content_type': 'image/jpeg'}
        ]
        }
         """
        files = self.request.files
        for file in files:
            filearr = files[file]
            for fileobj in filearr:
                # 存储
                file_path = os.path.join(config.BASE_DIRS, "upload/" + fileobj.filename)
                with open(file_path, "wb") as f:
                    f.write(fileobj.body)
        hobby = self.get_body_arguments("hobby", strip=True)
        print(username, password, hobby, files)  # 李龙飞 123 ['pain', 'read', 'run']
        self.write("okk")

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
<form action="/reget" method="post" enctype="multipart/form-data">
    姓名:<input type="text" name="username">
    <hr>
    密码:<input type="password" name="password">
    <hr>
    文件:<input type="file" name="file">
    <hr>
    爱好:
    <input type="checkbox" value="pain" name="hobby">画画
    <input type="checkbox" value="read" name="hobby">读书
    <input type="checkbox" value="run" name="hobby">跑步
    <input type="submit" name="登录"><span>第{{count}}次登录</span>
</form>
</body>
</html>

class StudentsHandler(RequestHandler):
    """
    数据库操作
    """
    def get(self, *args, **kwargs):
        sql = "select sname, gender from student"
        student_list = self.application.db.get_all(sql)
        print(student_list)
        self.render("student_show.html", student_list=student_list)

student_show.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>学生列表</title>
</head>
<body>
<ul>
    <li style="color: red">姓名--------性别</li>
    {% for student in student_list %}
     <li>{{ student["sname"] }} ------->{{ student["gender"] }}</li>
    {% end %}
</ul>
</body>
</html>

class CookieHandler(RequestHandler):
    """
    self.set_cookie(
    name="",  # cookie名
    value="",  # cookie值
    domain="",  # 提交cookie时匹配的域名
    expires=None,  # 设置有效期,可以是时间戳,时间元组,datetime型, 为UTC时间
    path="/",  # 提交cookie时默认的路径
     expires_days=5  # 设置有效期天数, 优先级低于expires
     )
     执行清除cookie后,并不是立即删除历览器的cookie,而是给cookie值设置为空,并改变其有效期限为失效,真正删除cookies是由浏览器自己去清理的

    """
    def get(self, *args, **kwargs):
        self.set_cookie("li", "youxiu")  # 实质上是调用了self.set_header("Set-Cookie", "li=youxiu;path=/")
        # self.get_cookie("li", "未登录")  # 获取cookie
        # self.clear_cookie("li")  # 清除cookie
        # self.clear_all_cookies(path="/", domain=None)  # 删除同时匹配path和domain的所有cookie
        self.write("cookie")


class ScCookieHandler(RequestHandler):
    """
    uuid, base64
    secret = base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes)
    在settings中设置 "cookie_secret": "G0SAhaKwQ6eU/lqXoW+b3g/at/cy1EKDluw/JcFlYtA=",
    self.set_secure_cookie(name="", value="", expires_days=30, version=None, **kwargs)
    self.get_secure_cookie(name="", value="", max_age_days=31, min_version=None)  #  max_age_days:过滤出在这个期限内的时间

    """
    def get(self, *args, **kwargs):
        self.set_secure_cookie("key", "123")  # 有问题, 设置不上
        self.write("...")


class CookieCountHandler(RequestHandler):
    """
    cookie计数
    """
    def get(self, *args, **kwargs):
        count = self.get_cookie("count", default=None)
        if count:
            count = int(count)
            count += 1
        else:
            count = 1
        self.set_cookie("count", str(count), expires=1)
        self.write("第{}次访问".format(str(count)))


class XsrfHandler(RequestHandler):
    """
    setting中设置:"xsrf_cookie": True
    模板中添加{% module xsrf_form_html() %}
    也可以发ajax请求:
        $.ajax({
            .....
            headers:{
                "X-XSRFToken": "xsrf_token"
            }

        })
    """
    def get(self, *args, **kwargs):
        count = self.get_cookie("count", default=None)
        if not count:
            count = 1
        self.render("postfilexsrf.html", count=count)

    def post(self, *args, **kwargs):
        count = self.get_cookie("count", default=None)
        if count:
            count = int(count)
            count += 1
        else:
            count = 1
        self.set_cookie("count", str(count))
        self.redirect("/xsrf")

postfilexsrf.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
<form action="/xsrf" method="post" enctype="multipart/form-data">
    姓名:<input type="text" name="username">
    <hr>
    密码:<input type="password" name="password">
    <hr>
    文件:<input type="file" name="file">
    <hr>
    爱好:
    <input type="checkbox" value="pain" name="hobby">画画
    <input type="checkbox" value="read" name="hobby">读书
    <input type="checkbox" value="run" name="hobby">跑步
    <input type="submit" name="登录"><span>第{{count}}次登录</span>
</form>

<script type="text/javascript" src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
    function getCookie(name){
        var $cookie = document.cookie.match("\\b" + name + "=([^;]*)\\b")
        return $cookie ? $cookie[1]:undefined
    }
    $.ajax({
        url: "",
        method: "POST",
        data: {

        },
        success:function (data) {

        },
        header: {
            "X_XSRFToken": getCookie("_xsrf")
        }
    })
</script>
</body>
</html>

class SetXsrfHandler(RequestHandler):
    def get(self, *args, **kwargs):
        self.xsrf_token
        self.finish()


class StaticFileHandler(web.StaticFileHandler):
    """
    在主页就设置"_xsrf"
    """
    def __init__(self, *args, **kwargs):
        super(StaticFileHandler, self).__init__(*args, **kwargs)
        self.xsrf_token


class LoginHandler(RequestHandler):
    """
    登录界面
    """
    def get(self, *args, **kwargs):
        next = self.get_argument("next", "/")
        url = "login?next=" + next
        self.render("login2.html", url=url)
    def post(self, *args, **kwargs):
        next = self.get_argument("next", "/")
        name = self.get_body_argument("username")
        passwd = self.get_body_argument("password")
        print(name, passwd)
        if name == "long" and passwd == "123":
            print(next)
            self.redirect(next + "?flag=logined")
        else:
            self.redirect("/login?next=" + next)

login2.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
<form action="{{ url }}" method="post" enctype="multipart/form-data">
    {% module xsrf_form_html() %}
    姓名:<input type="text" name="username">
    <hr>
    密码:<input type="password" name="password">
    <hr>
    文件:<input type="file" name="file">
    <hr>
    爱好:
    <input type="checkbox" value="pain" name="hobby">画画
    <input type="checkbox" value="read" name="hobby">读书
    <input type="checkbox" value="run" name="hobby">跑步
    <input type="submit" name="登录"><span></span>
</form>
</body>
</html>

class AuthHandler(RequestHandler):
    """
    认证
    """
    def get_current_user(self):
        """
        若返回为Ture则验证成功
        若返回值为False则重定向到settings 中login_url所指定的路由
        :return:
        """
        flag = self.get_argument("flag", default="")
        return flag

    @web.authenticated  # 认证装饰器
    def get(self, *args, **kwargs):
        self.write("你好啊")


# class CallbackAsy(RequestHandler):
#     """
#     用回调函数实现异步请求(这个有问题)
#     """
#     def on_response(self, response):
#         if response.error:
#             self.send_error(500)
#         else:
#             data = response.body
#             self.write(data)
#         self.finish()
#
#     @web.asynchronous
#     def get(self, *args, **kwargs):
#         url = "www.baidu.com"
#         client = AsyncHTTPClient()  # 创建客户端
#         client.fetch(url, self.on_response)


class GenvenletAsy(RequestHandler):
    """
    用协程实现异步请求
    """
    @web.gen.coroutine
    def get(self, *args, **kwargs):
        url = "https://www.baidu.com"
        client = AsyncHTTPClient()  # 创建客户端
        res = yield client.fetch(url)
        if res.error:
            self.send_error(500)
        else:
            data = res.body
            self.write(data)


class GenvenletAsy2(RequestHandler):

    def get(self, *args, **kwargs):
        res = yield self.getData()
        self.write(res)

    @web.gen.coroutine
    def getData(self):
        url = "www.baidu.com"
        client = AsyncHTTPClient()
        data = yield client.fetch(url)
        if data.error:
            data = {"ret": 0}
        else:
            data = data.body
        raise web.gen.Return(data)


class HomeHandler(RequestHandler):
    def get(self, *args, **kwargs):
        self.render("home.html")


class ChatHandler(WebSocketHandler):
    user = []  # 存储每一个人的信息

    def open(self):
        """
        websocket连接建立后执行
        :return:
        """
        self.user.append(self)
        for user in self.user:
            user.write_message("[{}]登录了".format(self.request.remote_ip))

    def on_message(self, message):
        """
        当客户端发送消息过来时调用
        :param message:
        :return:
        """
        for user in self.user:
            user.write_message("[{}]说:{}".format(self.request.remote_ip, message))
    #
    def on_close(self):
        """
        当websocket链接关闭后调用
        :return:
        """
        self.user.remove(self)
        for user in self.user:
            user.write_message("[{}]退出了".format(self.request.remote_ip))
    #
    # def write_message(self, message, binary=False):
    #     """
    #     主动向客户端发送消息
    #     :param message: 可以是字符串,或者字典(转成json字符串),
    #     :param binary:如果binary为false,则message会以UTF-8编码发送,为Ture发送二进制,字节码
    #     :return:
    #     """
    #     pass

    def close(self, code=1001, reason="123"):
        """
        关闭websocket链接, 服务器主动关闭
        :param code:
        :param reason:
        :return:
        """
        pass

    def check_origin(self, origin):
        """
        判断源origin, 对于符合的请求允许链接,同源策略
        :param origin:
        :return:
        """
        return True

home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>聊天界面</title>
</head>
<body>
<div id="contents" style="width: 500px;height: 500px;overflow: auto">

<div>
    <input type="text" id="message">
    <button id="send">发送</button>
</div>
</div>
<script type="text/javascript" src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
    var ws = new WebSocket("ws://127.0.0.1:8000/chat"); // 建立连接


    ws.onmessage = function(e){
        $("#contents").append("<p>" + e.data + "</p>")
    };  // 接收服务器消息


    $("#send").on("click", function () {
        var $mes = $("#message");
        var message = $mes.val();
        ws.send(message);
        $mes.val("")
    })  // 向服务器发消息
</script>

</body>
</html>

猜你喜欢

转载自blog.csdn.net/qq_41433183/article/details/90732977