python notes (tornado acquaintance)

A, tornado Introduction

  1. Lightweight web framework

  2. Asynchronous non-blocking IO handling

  3. Excellent resistance to load capacity

  4. Excellent processing performance, not dependent on multi-process / multi-threaded, to some extent, solve the problem GIL

  5. WSGI full stack of alternative products, is recommended while using their web framework and HTTP server

  6. socket connection establishment

    Here Insert Picture Description

  7. tornado life cycle
    Here Insert Picture Description

Second, the use of simple preliminary

  1. httpserver form

    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. Multi-process form

    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. Common forms

    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. With options using a form (in three ways)

    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 (second approach)

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

    config.py (third approach)

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

Third, asynchronous IO

  1. Coroutine asynchronous 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()
    

    Output:

    Started reaA
    start delay operation. . .
    Started reaB
    end of the implementation reaA
    end latency operation. . .
    Data received data I sent the
    end of the implementation reaA

  2. Plus asynchronous IO callbacks

    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()
    

    Output:

    Started reaA
    start delay operation. . .
    End execution reaA
    started reaB
    start delay operation. . .
    End execution reaB
    started reaC
    start delay operation. . .
    End execution reaC
    end latency operation. . .
    The end of the time delay. . .
    Receiving a callback function receives the value of longIo
    end latency operation. . .
    Receiving a callback function receives the value of longIo
    received callback function receives the value of longIo

Four, tornado basic framework

Here Insert Picture Description

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 (only add functionality)

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>

Guess you like

Origin blog.csdn.net/qq_41433183/article/details/90732977