Tornado-基于正则的路由和动态分页

概览

这一小节涉及了三部分内容:

1.动态分页设计

2.基本的路由系统以及基于正则的路由

3.模块引擎的继承和导入

4.web项目文件夹和ReuquestHandler的分类

5.跨站脚本攻击

文件结构

 

Python代码

start.py

from tornado.ioloop import IOLoop
import tornado.web
from controllers import account
from controllers import home
from controllers import extend


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


# 路由映射
# 基于正则路由是为了解决基本路由僵化的一一对应问题,可以实现一个类处理多种url
application = tornado.web.Application([
    #
    (r"/index/?(?P<page>d*)", home.IndexHandler),
    (r"/login", account.LoginHandler),
    # 模板继承与导入
    (r"/extend/index", extend.ExtendIndexHandler),
    (r"/extend/home", extend.ExtendHomeHandler),
], **settings)


# 启动服务端
if __name__ == "__main__":
    application.listen(8888)
    IOLoop.instance().start()
View Code

controllers -account.py -extend.py -home.py

import tornado.web


class LoginHandler(tornado.web.RequestHandler):
    def get(self):
        pass


class LogoutHandler(tornado.web.RequestHandler):
    def get(self):
        pass


class RegisterHandler(tornado.web.RequestHandler):
    def get(self):
        pass
account.py
import tornado.web


class ExtendIndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('extend/index.html')


class ExtendHomeHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('extend/home.html')
extend.py
import tornado.web
from commons.pagination import Pagination

INFO_LIST = [
    {'name': 'yeff', 'age': '23'},
]
for i in range(99):
    INFO_LIST.append({
        'name': 'a'+str(i), 'age': '0'+str(i)
    })


class IndexHandler(tornado.web.RequestHandler):
    def get(self, page):
        pn = Pagination(page, 5, INFO_LIST)
        str_pages = pn.get('/index')
        display_list = pn.items[pn.start_item:pn.end_item]
        self.render("home/index.html", info_list=display_list, current_page=page, page_nums=str_pages)

    def post(self, page):
        # 之前提交时,action连接的是/index,因此在没有添加current_page参数之前,page都只会为空字符串
        name = self.get_argument('name')
        age = self.get_argument('age')
        _dic = {'name': name, 'age': age}
        INFO_LIST.append(_dic)
        self.redirect("/index/" + page)
home.py

commons -Pagination.py

class Pagination(object):
    def __init__(self, current_page, items_per_page, items):
        try:
            current_page = int(current_page)
            assert current_page > 0
            assert current_page < len(items)
        except:
            current_page = 1
        self.current_page = current_page
        self.items = items
        self.per_page = items_per_page

    @property
    def start_item(self):
        # 设若有n个元素,每页显示a个,则第x页的元素列表就是
        # 前一页的最后一个元素是第(x-1)*a个元素,下一个就是第[(x-1)*a]+1个元素,序号就是(x-1)*a
        return (self.current_page - 1) * self.per_page

    @property
    def end_item(self):
        # 当前页的最后一个元素是第ax个,序号就是ax-1,但因为切片操作留头切尾,因此还要再+1,结果就是ax
        return self.current_page * self.per_page

    # Create page numbers
    def get(self, base_url=''):
        actual_pages, _remainder = divmod(len(self.items), self.per_page)
        actual_pages += 1 if _remainder > 0 else 0

        start = self.current_page - 5
        end = self.current_page + 5

        # 防止页码变成负数,以及大于实际页数
        # 这里的条件限制类似放大镜那一节防止超出边界时的设置
        # 这里用到了多重赋值和三元运算符
        # 多重赋值本质就是tuple packing(元组打包)和 Sequence unpacking(序列解包)
        # A = Y if X else Z
        # 只有当X为真时才会执行表达式Y,而只有当X为假时,才会执行表达式Z
        start, end = (1, 10) if start <= 0 else (start, end)
        start, end = (end-10, actual_pages) if end >= actual_pages else (start, end)

        pages_list = []

        # 首页标签
        first_page = '<a href="%s/1">首页</a>' % base_url
        # 上一页标签
        prev_page = '<a href="%s/%s">上一页</a>' % (base_url, self.current_page - 1 if self.current_page > 1 else self.current_page )

        pages_list.append(first_page+prev_page)
        for _ in range(start, end + 1):
            if _ == self.current_page:
                _str = '<a class="active" href="%s/%s">%s</a>' % (base_url, _, _)
            else:
                _str = '<a href="%s/%s">%s</a>' % (base_url, _, _)
            pages_list.append(_str)

        # 下一页标签
        next_page = '<a href="%s/%s">下一页</a>' % (base_url, self.current_page + 1 if self.current_page < actual_pages else self.current_page)
        # 尾页标签
        last_page = '<a href="%s/%s">首页</a>' % (base_url, actual_pages)
        pages_list.append(next_page+last_page)

        # 跳转标签和输入框
        # 注意嵌套情况下引号的使用:onclick后的引号,其参数的引号
        # 注意js的形参不能是已占用的关键字this
        jump = """<input type='text'/><a onclick="Jump('%s',this);">跳转</a>""" % base_url
        script = """<script>
            function Jump(base_url, ths){
                val = ths.previousElementSibling.value;
                if(val.trim().length>0){
                    location.href=base_url + "/" + val;
                }
            }
        </script>"""
        pages_list.append(jump+script)

        pages = ''.join(pages_list)
        return pages
View Code

HTML文件

home -index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
    <style>
        .pages a{
            display:inline-block;
            padding:5px;
            margin:5px;
            background-color:yellow;
        }
        .pages .active {background-color:red;}
    </style>
</head>
<body>
    <h1>输入数据</h1>
    <form action="/index/{{ current_page }}" method="post">
        <input type="text" name="name">
        <input type="text" name="age">
        <input type="submit" value="提交">
    </form>
    <h1>展示数据</h1>
    <table border="1">
        <th>
            <tr>
                <td>姓名</td>
                <td>年龄</td>
            </tr>
        </th>
        <tbody>
            {% for item in info_list %}
            <tr>
                <!--raw行就是一个XSS跨站脚本攻击的一个例子,本来tornado内部会对js脚本默认进行处理,避免脚本攻击‘-->
                <!--如果加上raw的话,就会以输入的格式进行处理-->
                <!--如:输入<script>alert(1);</script>-->
                <!--提交后,渲染页面时,遇到这一行就会执行该代码-->
                <!--使用toranado的默认方式就好-->
                <!--<td>{% raw item['name'] %}</td>-->
                <td>{{ item['name'] }}</td>
                <td>{{ item['age'] }}</td>
            </tr>
            {% end %}
        </tbody>
    </table>
    <div class="pages">
        {% raw page_nums %}
    </div>
</body>
</html>
主页面(动态分页)

plates -form.html -layout.html

<form>
    <!--模板导入-->
    <h1>Form To Be Included</h1>
    <input type="text"/>
    <input type="password"/>
    <input type="button">
    <input type="submit">
</form>
form.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>模板继承</title>
</head>
<style>
    *{color:white;}
    .pg-header{
        width:800px;
        height:200px;
        background-color:gray;
    }
    .pg-content{
        width:800px;
        height:500px;
        background-color:green;
    }
    .pg-footer{
        width:800px;
        height:100px;
        background-color:orange;
    }
</style>
        {% block css %}{% end %}
<body>
    <div class="pg-header"></div>
    <div class="pg-content">
        {% block body %}{% end %}
    </div>
    <div class="pg-footer"></div>
<script> function MainPage(){alert("Layout");}</script>
{% block js %}{% end %}
</body>
</html>
layout.html

extend -home.html -index.html(主要是为了应用模板文件,展示模板引擎的继承和导入)

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

{% block css %}
    <style>a{color:yellow;}</style>
{%  end  %}

{% block body %}
    <h1>Home Page</h1>
    <a>Home page</a>
{%  end  %}

{% block js %}
    <script>
        alert("Home  page.");
    </script>
{%  end  %}
home.html
{% extends '../plates/layout.html' %}

{% block body %}
    <h1>Index Page</h1>
    <a>Index Page</a>
    {% include '../plates/form.html'%}
{%  end  %}

{% block css %}
    <style>
        a{color:black;}
    </style>
{%  end  %}

{% block js %}
    <script>
        alert("Index page.");
    </script>
{%  end  %}
index.html
原文地址:https://www.cnblogs.com/yifeixu/p/8028549.html