web框架详解之tornado 三 url和分页
一、代码目录构建
controllers :处理业务逻辑的
account:处理账户相关的
上面目录作用和内容
controllers 包 :处理业务逻辑的
account:处理账户相关的
home是主页内容文件
settings 包:内容设置等
Setting:配置文件
statics 包: 静态文件的下相关目录
views 包: HTML文件包
内容分别为:
#/usr/bin/env python #-*-coding:utf-8 -*- import tornado.web class LoginHandler(tornado.web.RequestHandler): def get(self,*args,**kwargs): self.write("ok") class LogoutHandler(tornado.web.RequestHandler): def get(self, *args, **kwargs): self.write("ok") class RegisterHandler(tornado.web.RedirectHandler): def get(self,*args,**kwargs): self.write("ok")
#/usr/bin/env python #-*-coding:utf-8-*- import tornado.web class IndexHandler(tornado.web.RequestHandler): def get(self, *args, **kwargs): # self.write("index.html") self.render("index.html")
#/usr/bin/env python #-*-coding:utf-8-*- settings={ "template_path":"views",#模板路径的配置 "static_path":"statics" #静态文件 }
#/bin/usr/env python #-*- coding:utf-8 -*- import tornado.ioloop import tornado.web from controllers import home from settings import Setting #路由映射,路由系统 application=tornado.web.Application( [(r"/index",home.IndexHandler),], **Setting.settings ) if __name__=="__main__": application.listen(8000) tornado.ioloop.IOLoop.instance().start()
其实就是把tornado拆解了
#!/usr/bin/env python # -*- coding:utf-8 -*- # 首先导入模块 import tornado.ioloop import tornado.web # 让这个类继承 执行 class MainHandler(tornado.web.RequestHandler): def get(self): self.render("s1.html") # 路由映射,也可以叫做路由系统 application = tornado.web.Application([ (r"/index", MainHandler), ]) if __name__ == "__main__": application.listen(8000) tornado.ioloop.IOLoop.instance().start()
二、基于正则的动态路由
设置分页:在路由系统中,用正则匹配数字,并且让第一个明明为num,第二个为nid
#路由映射,路由系统 application=tornado.web.Application( [(r"/index/(?P<num>\d*)/(?P<nid>\d*)",home.IndexHandler),], **Setting.settings )
#/usr/bin/env python #-*-coding:utf-8-*- import tornado.web class IndexHandler(tornado.web.RequestHandler): def get(self,nid,num): # self.write("index.html") print(nid,num) self.render("index.html")
在网页url中输入http://127.0.0.1:8000/index/12321/151
在后台中输出 151 和12321
三、路由系统之二级域名
application.add_handlers("www.cnblogs.com",[ (r"/index/(?P<page>\d*)",这里是自己定义的类) ])
二级路由就是:首先匹配域名,然后再匹配域名下的各个页面
一级路由是直接匹配各个页面
四、自定义分页:
redirect_to实现的是action方法的跳转,向浏览器发起一个新的请求,具体使用方法如下
注意点:用户连接tornado的时候,这个框架会用get方法给服务端发送页面信息,然后客户端向服务端发送消息,这里是用post方式,从客户端发送消息到服务端
#/bin/usr/env python #-*- coding:utf-8 -*- import tornado.ioloop import tornado.web from controllers import home from settings import Setting #路由映射,路由系统 application=tornado.web.Application( [(r"/index/(?P<page>\d*)",home.IndexHandler),], **Setting.settings ) if __name__=="__main__": application.listen(8000) tornado.ioloop.IOLoop.instance().start()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <h1>提交数据</h1> <form method="post" action="/index/1"> <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> </body> </html>
#/usr/bin/env python #-*-coding:utf-8-*- import tornado.web LIST_INFO=[ {"username":"aa","email":"pyrene3110436742@162.com"} ] class IndexHandler(tornado.web.RequestHandler): def get(self,page): #假如每页显示5条数据 # page是当前页 # 第一页:0:5 LIST_INFO[0:5] #第二页:5:10 LIST_INFO[5:10] try: page=int(page) except Exception: page=1 if page<1: page=1 start=(page-1)*5 end=page*5 current_list=LIST_INFO[start:end] # print(nid,num) self.render("home/index.html" ,list_info=current_list) def post(self, *args, **kwargs): user=self.get_argument("username") email=self.get_argument("email") temp={"username":user,"email":email} LIST_INFO.append(temp) self.redirect("/index/1")
分析:
这里有两种内容
1、用户通过表格form提交数据,通过post方法,把输入的内容添加到LIST_INFO列表中,并且通过redirect方法跳转到get方法中返回给客户端
2、内部做了页面判断,并且让页面只显示5条数据,用户通过路由系统输入url来访问每一页
自定义分页优化
通过传值来记住当前页
三中的bug问题点在于:页面输入的时候,会跳转到首页,要解决
#/usr/bin/env python #-*-coding:utf-8-*- import tornado.web LIST_INFO=[ {"username":"aa","email":"pyrene3110436742@162.com"} ] class IndexHandler(tornado.web.RequestHandler): def get(self,page): #假如每页显示5条数据 # page是当前页 # 第一页:0:5 LIST_INFO[0:5] #第二页:5:10 LIST_INFO[5:10] try: page=int(page) except Exception: page=1 if page<1: page=1 start=(page-1)*5 end=page*5 current_list=LIST_INFO[start:end] # print(nid,num) self.render("home/index.html" ,list_info=current_list,current_page=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> </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> </body> </html>
后台get方法中自定义页码变量,传入到html中,之后用户输入触发post方法,然后在post方法中跳转到当前页面
自定义分页二:实现下图中的分页
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <style> .pager a{ display: inline-block; padding: 5px; margin: 3px; background-color: cadetblue; } .pager a.active{ background-color:brown ; 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>{% raw line['username']%}</td> <td>{{line['email']}}</td> </tr> {% end %} </tbody> </table> <div class="pager"> {% raw str_page %} </div> </body> </html>
#/usr/bin/env python #-*-coding:utf-8-*- import tornado.web LIST_INFO=[ {"username":"aa","email":"pyrene3110436742@162.com"} ] for i in range(99): temp={"username":"bb"+str(i),"email":str(i)+"123"} LIST_INFO.append(temp) class IndexHandler(tornado.web.RequestHandler): def get(self,page): #假如每页显示5条数据 # page是当前页 # 第一页:0:5 LIST_INFO[0:5] #第二页:5:10 LIST_INFO[5:10] try: page=int(page) except Exception: page=1 if page<1: page=1 start=(page-1)*5 end=page*5 current_list=LIST_INFO[start:end] all_pager,c=divmod(len(LIST_INFO),5) if c>0: all_pager+=1 list_page=[] for p in range(all_pager): # 设置当前页的样式 if p+1 ==page: temp='<a class="active" href="/index/%s">%s</a>' %(p+1,p+1) else: temp='<a href="/index/%s">%s</a>'%(p+1,p+1) list_page.append(temp) str_page="".join(list_page) # print(nid,num) self.render("home/index.html" ,list_info=current_list,current_page=page,str_page=str_page)
1 实现步骤: 2 首先在后台计算出数据的总页数,用divemode的方法 3 all_pager,c=divmod(len(LIST_INFO),5) 4 if c>0: 5 all_pager+=1 6 all_pager为整数,c为小数,这里判断,如果c为小数的时候让整数加上1,也就是需要显示的页数 7 2、创建一个空列表,并且遍历计算出来的页数,返回给前端页面 8 list_page=[] 9 10 for p in range(all_pager): 11 # 设置当前页的样式 12 if p+1 ==page: 13 temp='<a class="active" href="/index/%s">%s</a>' %(p+1,p+1) 14 else: 15 temp='<a href="/index/%s">%s</a>'%(p+1,p+1) 16 list_page.append(temp) 17 18 str_page="".join(list_page) 19 20 # print(nid,num) 21 self.render("home/index.html" ,list_info=current_list,current_page=page,str_page=str_page) 22 注意点: 23 1、这里要设置点击到那一页的时候就显示那一页的样式,用于区分 24 2、由于要传给前台的是字符串,所以这里要用join方法分割成字符串 25 3、pag参数为当前页码 26 27 前台代码: 28 <div class="pager"> 29 {% raw str_page %} 30 </div> 31 由于后台传过来的数据,要按照后台的样式展示给用户,所以这里要用原声的js显示 32 这里注意,后台要显示的原生的js必须是指定范围的,不然会被攻击
自定义分页优化
优化如下:
具体思想如下: all_pager:总页数 current_pager:当前页 # range(当前页-5,当前页+5+1) 当 总页数<11的时候 1、 显示总页数 当 总页数>11的时候 如果当前页<=6: 显示前11页 如果当前页>6: 如果:当前页+5 >总页数: 总页数 -11 , 总页数 else: 当前页-5, 当前页+5
#s t 分页逻辑,这里让其显示11条信息 if all_pager<11: s=1 t=all_pager else: if page<=6: s=1 t=11 else: if(page+5)>all_pager: s=all_pager-10 t=all_pager else: s=page-5 t=page+5 #注意这里的p代表的是s和t+1之间的页码 for p in range(s,t+1): # 设置当前页的样式 if p ==page: temp='<a class="active" href="/index/%s">%s</a>' %(p,p) else: temp='<a href="/index/%s">%s</a>'%(p,p) list_page.append(temp) str_page="".join(list_page)
如何做成插件?
把各个方法分别封装到类中,然后如果以后想用这个插件,直接导入这个插件中的类就可以
如果要封装页码:需要思考需求和产出什么
需求:总页数、当前页
产出:start end str_page
把方法封装到类中,类中分别对应方法
#/usr/bin/env python #-*-coding:utf-8-*- import tornado.web LIST_INFO=[ {"username":"aa","email":"pyrene3110436742@162.com"} ] for i in range(99): temp={"username":"bb"+str(i),"email":str(i)+"123"} LIST_INFO.append(temp) class Pagination: #下面封装的参数分别为,当前页数,和总的数据 def __init__(self,current_page,all_item): #初始化当前页和总页数 all_pager,c=divmod(all_item,5) #all_pager为计算的到的总页数,要经过下面的判断 if c>0: all_pager+=1 self.all_pager=all_pager try: #为当前页处理异常 current_page=int(current_page) except Exception: current_page=1 if current_page<1: current_page=1 self.current_page=current_page # 加上这样的装饰器,会使下面调用这个方法的时候以访问字段的形式来访问,也就是不用加上括号 @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=[] if self.all_pager<11: s=1 t=self.all_pager else: if self.current_page<=6: s=1 t=11 else: if( self.current_page+5)>self.all_pager: s=self. current_page-10 t=self. current_page else: s= self.current_page-5 t= self.current_page+5 for p in range(s,t+1): if p == self.current_page: temp='<a class="active" href="%s%s">%s</a>' %(base_url,p,p) #这里设置自定义url else: temp='<a href="%s%s">%s</a>'%(base_url,p,p) list_page.append(temp) return "".join(list_page) #直接返回值 class IndexHandler(tornado.web.RequestHandler): def get(self,page): page_obj=Pagination(page,len(LIST_INFO)) #传入当前页和总页数生成对象 current_list=LIST_INFO[page_obj.start:page_obj.end] #以字段的方式来应用类中的方法 str_page=page_obj.page_str("/index/") #传入自己自定义的url # 传入参数,当前页的参数和页码数的参数 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)
跳转页面
1 location.href=”url” 2 首页:尾页:上一页:下一页都不难 3 4 搜索跳转的代码形式: 5 只要执行上面的就会自动跳转到指定的url 6 所以这里用了js的事件方法,只不过把js变成字符串的形式传递给前台,然后转化为js代码的形式
#首页 first_page='<a href="%s1">首页</a>'%(base_url,) list_page.append(first_page) #上一页 current_page-1 在javascript中加上javascript:void(0)表示什么也不做 if self.current_page==1: pre_page='<a href="javascript:void(0)">上一页</a>' else: pre_page='<a href="%s%s">上一页</a>'%(base_url,self.current_page-1) list_page.append(pre_page) #中间页数 for p in range(s,t+1): if p == self.current_page: temp='<a class="active" href="%s%s">%s</a>' %(base_url,p,p) #这里设置自定义url else: temp='<a class-"active" href="%s%s">%s</a>'%(base_url,p,p) list_page.append(temp) #尾页 last_page='<a href=%s%s>尾页</a>'%(base_url,self.all_pager) list_page.append(last_page) #下一页 current_page+1 在javascript中加上javascript:void(0)表示什么也不做 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) #跳转页面 jump='''<input type="text" /><a onclick="jump('%s',this);">GO</a>'''%(base_url,) script='''<script> function jump(baseUrl,ths){ var val=ths.previousElementSibling.value; if(val.trim().length>0){ location.href=baseUrl+val } } </script>''' list_page.append(jump) list_page.append(script) return "".join(list_page)
五、安全XSS攻击
xss跨站脚本攻击
1 <h1>显示数据</h1> 2 <table border="1"> 3 <thead> 4 <tr> 5 <th>用户名</th> 6 <th>邮箱</th> 7 </tr> 8 </thead> 9 <tbody> 10 {% for line in list_info %} 11 <tr> 12 <!--<td>{{line['username']}}</td>--> 13 <td>{% raw line['username']%}</td> 14 <td>{{line['email']}}</td> 15 </tr> 16 {% end %} 17 </tbody> 18 </table>
上面13行的就是以原始方式展示,没做处理,那么无论用户还是后台输入的js代码都会以js形式展示给用户
这种就是xss跨站脚本攻击模式