概览
这一小节涉及了三部分内容:
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()
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
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')
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)
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
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>
<!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>
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 %}
{% 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 %}