tornado 框架一( tornado 路由系统,模板引擎,分页,表单验证)
1.Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。
2.基础代码
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world") application = tornado.web.Application([ (r"/index", MainHandler), ]) if __name__ == "__main__": application.listen(80) tornado.ioloop.IOLoop.instance().start()
静态文件的配置
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.render('index.html') # 在template 目录下 setting = { # 配置tempalate_path, 系统去template 下找模板文件(html) 'template_path':'template', # 配置 static_path ,系统去static 目录下去找静态文件 (js ,css,image) 'static_path':'static', # 配置了这个,如果配置成其他的前缀/sss/ ,即使有static 这个目录,访问时 用href='sss/c1.css' 'static_url_prefix':'/static/', #<link rel="stylesheet" href="static/c1.css"> } #路由映射 路由系统 application = tornado.web.Application([ (r"/index", MainHandler), ],**setting) if __name__ == "__main__": application.listen(80) tornado.ioloop.IOLoop.instance().start()
目录结构:

客户端缓存文件
<link rel="stylesheet" href="{{static_url('ind_c.css')}}">
3.模板引擎的使用
html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="static/c1.css"> <script src="static/j1.js"></script> </head> <body> <form action="/index" method="post"> <input type="text" name="text"> <input type="submit" value="提交"> </form> <ul> {% for item in listabc %} <li>{{item}}</li> {% end %} </ul> </body> </html>
python:
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web lista = [] class MainHandler(tornado.web.RequestHandler): def get(self): # self.render('index.html') # 在template 目录下 self.render('index.html', listabc=lista) def post(self, *args, **kwargs): lista.append(self.get_argument('text')); self.render('index.html',listabc= lista) setting = { # 配置tempalate_path, 系统去template 下找模板文件(html) 'template_path':'template', # 配置 static_path ,系统去static 目录下去找静态文件 (js ,css,image) 'static_path':'static', # 配置了这个,如果配置成其他的前缀/sss/ ,即使有static 这个目录,访问时 用href='sss/c1.css' 'static_url_prefix':'/static/', #<link rel="stylesheet" href="static/c1.css"> } #路由映射 路由系统 application = tornado.web.Application([ (r"/index", MainHandler), ],**setting) if __name__ == "__main__": application.listen(80) tornado.ioloop.IOLoop.instance().start()
4.模板引擎的本质 bUIMethod以UIModule
定义
def func(self): # 必须要给self参数 return 'uimethod func'
from tornado.web import UIModule from tornado import escape class classA(UIModule): def render(self, *args, **kwargs): return 'uimodule classA'
配置
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web import uimethod as mt import uimodule as md lista = [] class MainHandler(tornado.web.RequestHandler): def get(self): # self.render('index.html') # 在template 目录下 self.render('index.html', listabc=lista,abc='ABC') def post(self, *args, **kwargs): lista.append(self.get_argument('text',None));# 获取不到值时 取空 self.render('index.html',listabc= lista) setting = { # 配置tempalate_path, 系统去template 下找模板文件(html) 'template_path':'template', # 配置 static_path ,系统去static 目录下去找静态文件 (js ,css,image) 'static_path':'static', # 配置了这个,如果配置成其他的前缀/sss/ ,即使有static 这个目录,访问时 用href='sss/c1.css' 'static_url_prefix':'/static/', #<link rel="stylesheet" href="static/c1.css"> #配置模板语言 ui_methods 'ui_methods':mt, # 配置模板语言 ui_modules 'ui_modules':md, } #路由映射 路由系统 application = tornado.web.Application([ (r"/index", MainHandler), ],**setting) if __name__ == "__main__": application.listen(80) tornado.ioloop.IOLoop.instance().start()
使用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="static/c1.css"> <script src="static/j1.js"></script> </head> <body> <form action="/index" method="post"> <input type="text" name="text"> <input type="submit" value="提交"> </form> <ul> {% for item in listabc %} {% if item == 'xxx'%} <li style="color: green">{{item}}</li> {% else %} <li>{{item}}</li> {% end %} {% end %} </ul> <div>{{abc}}</div> <div>{{func()}}</div> <div>{% module classA()%}</div> </body> </html>
在模板中默认提供了一些函数、字段、类以供模板使用:
escape:tornado.escape.xhtml_escape的別名xhtml_escape:tornado.escape.xhtml_escape的別名url_escape:tornado.escape.url_escape的別名json_encode:tornado.escape.json_encode的別名squeeze:tornado.escape.squeeze的別名linkify:tornado.escape.linkify的別名datetime: Python 的datetime模组handler: 当前的RequestHandler对象request:handler.request的別名current_user:handler.current_user的別名locale:handler.locale的別名_:handler.locale.translate的別名static_url: forhandler.static_url的別名xsrf_form_html:handler.xsrf_form_html的別名
5.模板语言替换过程的本质
将HTML 转换为函数

#模板语言 替换的本质 用的字符串的拼接,再执行 exec namespace = {'name':'sb','age':[18,20,25]} code ='''def myfunc():return "name %s ,age %d"%(name,age[0])''' func = compile(code,'string','exec') exec(func,namespace) result = namespace['myfunc']() print(result)
6.cookie
设置,获取,清除cookie:
self.set_cookie('auth','ok',expires=time.time()+100) #expires=time.time()+100秒过期 expires_days=100 100天后过期
auth = self.get_cookie('auth')
self.clear_cookie('auth') # 清除cookie
cookie 加密:
Tornado的set_secure_cookie()和get_secure_cookie()函数发送和取得浏览器的cookies,以防范浏览器中的恶意修改。为了使用这些函数,你必须在应用的构造函数中指定cookie_secret参数。
配置文件 :
setting = {
'static_path':'static',
'template_path':'template',
'cookie_secret': 'abcdefg',
}
#!/usr/bin/env python # --*-- encoding:utf-8 --*-- import tornado.web import tornado.ioloop import time class IndexHandler(tornado.web.RequestHandler): def get(self, *args, **kwargs): self.render('index.html',login_fail='') def post(self, *args, **kwargs): username = self.get_argument('username') password = self.get_argument('password') if username=='abc' and password =='abc': self.set_cookie('auth','ok',expires=time.time()+100) #expires=time.time()+100秒过期 expires_days=100 100天后过期 self.redirect('/manager') else: self.render('index.html',login_fail='登陆失败') class ManagerHandler(tornado.web.RequestHandler): def get(self, *args, **kwargs): auth = self.get_cookie('auth') if auth == 'ok': self.render('manager.html') else: self.redirect('/index') class LogoutHandler(tornado.web.RequestHandler): def get(self, *args, **kwargs): self.clear_cookie('auth') # 清除cookie self.redirect('/index') setting = { 'static_path':'static', 'template_path':'template', 'cookie_secret': 'abcdefg', } application = tornado.web.Application([ (r'/index',IndexHandler), # 要加反斜杠 (r'/manager',ManagerHandler), (r'/logout',LogoutHandler), ],**setting) if __name__ == '__main__': application.listen(8000); tornado.ioloop.IOLoop.instance().start();
7.ajax
不刷新页面的情况下,与服务器进行数据交互
原生ajax 使用实例:
run.py
#!/usr/bin/env python # --*-- encoding:utf-8 --*-- import tornado.web import tornado.ioloop class LoginHandler(tornado.web.RequestHandler): def get(self, *args, **kwargs): self.render('login.html') def post(self, *args, **kwargs): username=self.get_argument('username') password=self.get_argument('password') dicta = {'login':True,'message':''} import json if username == '123' and password =='123': dicta['message'] = 'login sucess' else: dicta['login']=False dicta['message']='login false' #将字典转换为json 字符串 js =json.dumps(dicta) self.write(js) setting = { 'static_path':'static', 'template_path':'view', } application = tornado.web.Application([ (r'/login',LoginHandler), ],**setting) if __name__ == '__main__': application.listen('8000') tornado.ioloop.IOLoop.instance().start();
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="{{static_url('c1.css')}}"> </head> <body> <input id="user" type="text" name="username"> <input id="pwd" type="text" name="password"> <input class="sub" type="button" value="提交" onclick="submint();"> <script> xhr = null function submint() { //创建一个 XMLHttpRequest xhr = new XMLHttpRequest(); // 打开方式,地址,是否异步 xhr.open('post','/login',true); // 状态改变时调用函数 xhr.onreadystatechange = func //post方法要设置 请求头 xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); // 给服务器发送消息 xhr.send('username='+document.getElementById('user').value+';password='+document.getElementById('user').value); } function func(callback) { if(xhr.readyState==4){ //接受返回的消息 res = xhr.responseText; //将字符串转为字典 res_dict = JSON.parse(res); if(res_dict['login']){ alert(res_dict['message']); }else { alert(res_dict['message']); } } } </script> </body> </html>
jqureyajax
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="{{static_url('c1.css')}}"> </head> <body> <input id="user" type="text" name="username"> <input id="pwd" type="text" name="password"> <input class="sub" type="button" value="提交" onclick="submint();"> <script src="{{static_url('jquery-3.2.1.js')}}"></script> <script> function submint() { $.post('/login','username='+$('#user')+';password='+$('#pmd'),function (callback) { console.log(callback); }); } </script> </body> </html>
8.分页处理

index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .page_code a{ display: inline-block; padding: 5px; margin: 3px; border: 1px solid gainsboro; } .page_code a.sel{ background-color: red; color: white; } </style> </head> <body> <div>插入数据:</div> <form action="/index/{{c_page}}" method="post"> <input type="text" name="username"> <input type="password" name="password"> <input type="submit" value="插入"> </form> <br> <div>插入后的数据:</div> <table border="1"> <thead> <tr> <td>账号</td> <td>密码</td> </tr> </thead> <tbody> {% for item in list_info%} <tr> <td>{{item['username']}}</td> <td>{{item['password']}}</td> </tr> {% end %} </tbody> </table> <div class="page_code"> {% raw str_page_code %} </div> </body> </html>
index.py
#!/usr/bin/env python # --*-- encoding:utf-8 --*-- import tornado.web import tornado.ioloop LIST_INFO = [{'username':'admin','password':'admin'}] #模拟数据 测试 for i in range(100): tmp_dict = {'username':i,'password':'123'} LIST_INFO.append(tmp_dict) # 封装分页类 class pageination(): def __init__(self,current_page,total_page): try: current_page = int(current_page) except: current_page = 1 self.current_page = current_page self.total_page = total_page @property def start_data(self): return (self.current_page-1)*5 @property def end_data(self): return self.current_page*5 def get_page_str(self,base_dir): page_list= [] #生成当前页 前5页 后5页 start_c = None end_c = None if self.total_page<11: start_c = 1 end_c = self.total_page else: if self.current_page - 5 < 1: start_c = 1 end_c = 11 else: if self.current_page + 5 > self.total_page: start_c = self.total_page -10 end_c = self.total_page else: start_c = self.current_page -5 end_c = self.current_page +5 #首页 str_tem = '<a href="%s1">首页</a>' % (base_dir) page_list.append(str_tem) #上一页 if self.current_page == 1: pass else: str_tem = '<a href="%s%s">上一页</a>' % (base_dir,self.current_page-1) page_list.append(str_tem) #页码 for i in range(start_c,end_c+1): if i == self.current_page: str_tem = '<a class="sel" href="%s%s">%s</a>' % (base_dir,i,i) else: str_tem = '<a href="%s%s">%s</a>'%(base_dir,i,i) page_list.append(str_tem) #下一页 if self.current_page == self.total_page: pass else: str_tem = '<a href="%s%s">下一页</a>' % (base_dir,self.current_page+1) page_list.append(str_tem) # 尾页 str_tem = '<a href="%s%s"> 尾页</a>' % (base_dir, self.total_page) page_list.append(str_tem) #搜索 str_jump = """<input type="text"><a onclick="jump('%s',this);">跳转</a>""" %(base_dir,) str_script = """ <script> function jump(base_dir,ths) { var temp = ths.previousElementSibling.value; if(temp.trim().length>0){ location.href = base_dir + temp; } } </script> """ page_list.append(str_jump) page_list.append(str_script) #将list转字符串 page_str = ''.join(page_list) return page_str class IndexHandler(tornado.web.RequestHandler): #current_page 为当前页 def get(self, current_page): # 获取总页码 total_page, yu = divmod(len(LIST_INFO), 5) if yu > 0: total_page += 1 page_obj = pageination(current_page,total_page) #切片取当前页的数据 send_list = LIST_INFO[page_obj.start_data:page_obj.end_data] #渲染 self.render('index.html',list_info = send_list,c_page=current_page ,str_page_code=page_obj.get_page_str('/index/')) def post(self,current_page): username = self.get_argument('username') password = self.get_argument('password') tem_dict = {'username':username,'password':password} LIST_INFO.append(tem_dict) self.redirect('/index/'+current_page)
run.py
#!/usr/bin/env python # --*-- encoding:utf-8 --*-- import tornado.web import tornado.ioloop from controllers import index setting = { 'static_path':'statics', 'template_path':'views', } application = tornado.web.Application([ (r'/index/(?P<current_page>\d*)',index.IndexHandler), ],**setting) if __name__ == '__main__': application.listen('8000') tornado.ioloop.IOLoop.instance().start();
9.路由系统
正则路由与二级域名路由
application = tornado.web.Application([ (r"/index", IndexHandler), (r"/story/([0-9]+)", StoryHandler), ]) application.add_handlers('abc.baidu.com$', [ (r'/index',AbcHandler), ])
10.模板引擎的补充
1.导入 {% include 'head.html'%}
2.模板的继承 {% extends 'father.html'%}
母版
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>模板</title> <link href="{{static_url("css/common.css")}}" rel="stylesheet" /> {% block CSS %}{% end %} </head> <body> <div class="pg-header"> </div> {% block RenderBody %}{% end %} <script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script> {% block JavaScript %}{% end %} </body> </html>
子版
{% extends 'layout.html'%} {% block CSS %} <link href="{{static_url("css/index.css")}}" rel="stylesheet" /> {% end %} {% block RenderBody %} <h1>Index</h1> <ul> {% for item in li %} <li>{{item}}</li> {% end %} </ul> {% end %} {% block JavaScript %} {% end %}
11.表单验证
在Web程序中往往包含大量的表单验证的工作,如:判断输入是否为空,是否符合规则。
html:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <link href="{{static_url("commons.css")}}" rel="stylesheet" /> </head> <body> <h1>hello</h1> <form action="/index" method="post"> <p>hostname: <input type="text" name="host" /> </p> <p>ip: <input type="text" name="ip" /> </p> <p>port: <input type="text" name="port" /> </p> <p>phone: <input type="text" name="phone" /> </p> <input type="submit" /> </form> </body> </html>
py:
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web from hashlib import sha1 import os, time import re class MainForm(object): def __init__(self): self.host = "(.*)" self.ip = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$" self.port = '(\d+)' self.phone = '^1[3|4|5|8][0-9]\d{8}$' def check_valid(self, request): form_dict = self.__dict__ for key, regular in form_dict.items(): post_value = request.get_argument(key) # 让提交的数据 和 定义的正则表达式进行匹配 ret = re.match(regular, post_value) print key,ret,post_value class MainHandler(tornado.web.RequestHandler): def get(self): self.render('index.html') def post(self, *args, **kwargs): obj = MainForm() result = obj.check_valid(self) self.write('ok') settings = { 'template_path': 'template', 'static_path': 'static', 'static_url_prefix': '/static/', 'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh', 'login_url': '/login' } application = tornado.web.Application([ (r"/index", MainHandler), ], **settings) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start() Python
由于验证规则可以代码重用,所以可以如此定义:
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web import re class Field(object): def __init__(self, error_msg_dict, required): self.id_valid = False self.value = None self.error = None self.name = None self.error_msg = error_msg_dict self.required = required def match(self, name, value): self.name = name if not self.required: self.id_valid = True self.value = value else: if not value: if self.error_msg.get('required', None): self.error = self.error_msg['required'] else: self.error = "%s is required" % name else: ret = re.match(self.REGULAR, value) if ret: self.id_valid = True self.value = ret.group() else: if self.error_msg.get('valid', None): self.error = self.error_msg['valid'] else: self.error = "%s is invalid" % name class IPField(Field): REGULAR = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$" def __init__(self, error_msg_dict=None, required=True): error_msg = {} # {'required': 'IP不能为空', 'valid': 'IP格式错误'} if error_msg_dict: error_msg.update(error_msg_dict) super(IPField, self).__init__(error_msg_dict=error_msg, required=required) class IntegerField(Field): REGULAR = "^\d+$" def __init__(self, error_msg_dict=None, required=True): error_msg = {'required': '数字不能为空', 'valid': '数字格式错误'} if error_msg_dict: error_msg.update(error_msg_dict) super(IntegerField, self).__init__(error_msg_dict=error_msg, required=required) class CheckBoxField(Field): def __init__(self, error_msg_dict=None, required=True): error_msg = {} # {'required': 'IP不能为空', 'valid': 'IP格式错误'} if error_msg_dict: error_msg.update(error_msg_dict) super(CheckBoxField, self).__init__(error_msg_dict=error_msg, required=required) def match(self, name, value): self.name = name if not self.required: self.id_valid = True self.value = value else: if not value: if self.error_msg.get('required', None): self.error = self.error_msg['required'] else: self.error = "%s is required" % name else: if isinstance(name, list): self.id_valid = True self.value = value else: if self.error_msg.get('valid', None): self.error = self.error_msg['valid'] else: self.error = "%s is invalid" % name class FileField(Field): REGULAR = "^(\w+\.pdf)|(\w+\.mp3)|(\w+\.py)$" def __init__(self, error_msg_dict=None, required=True): error_msg = {} # {'required': '数字不能为空', 'valid': '数字格式错误'} if error_msg_dict: error_msg.update(error_msg_dict) super(FileField, self).__init__(error_msg_dict=error_msg, required=required) def match(self, name, value): self.name = name self.value = [] if not self.required: self.id_valid = True self.value = value else: if not value: if self.error_msg.get('required', None): self.error = self.error_msg['required'] else: self.error = "%s is required" % name else: m = re.compile(self.REGULAR) if isinstance(value, list): for file_name in value: r = m.match(file_name) if r: self.value.append(r.group()) self.id_valid = True else: self.id_valid = False if self.error_msg.get('valid', None): self.error = self.error_msg['valid'] else: self.error = "%s is invalid" % name break else: if self.error_msg.get('valid', None): self.error = self.error_msg['valid'] else: self.error = "%s is invalid" % name def save(self, request, upload_path=""): file_metas = request.files[self.name] for meta in file_metas: file_name = meta['filename'] with open(file_name,'wb') as up: up.write(meta['body']) class Form(object): def __init__(self): self.value_dict = {} self.error_dict = {} self.valid_status = True def validate(self, request, depth=10, pre_key=""): self.initialize() self.__valid(self, request, depth, pre_key) def initialize(self): pass def __valid(self, form_obj, request, depth, pre_key): """ 验证用户表单请求的数据 :param form_obj: Form对象(Form派生类的对象) :param request: Http请求上下文(用于从请求中获取用户提交的值) :param depth: 对Form内容的深度的支持 :param pre_key: Html中name属性值的前缀(多层Form时,内部递归时设置,无需理会) :return: 是否验证通过,True:验证成功;False:验证失败 """ depth -= 1 if depth < 0: return None form_field_dict = form_obj.__dict__ for key, field_obj in form_field_dict.items(): print key,field_obj if isinstance(field_obj, Form) or isinstance(field_obj, Field): if isinstance(field_obj, Form): # 获取以key开头的所有的值,以参数的形式传至 self.__valid(field_obj, request, depth, key) continue if pre_key: key = "%s.%s" % (pre_key, key) if isinstance(field_obj, CheckBoxField): post_value = request.get_arguments(key, None) elif isinstance(field_obj, FileField): post_value = [] file_list = request.request.files.get(key, None) for file_item in file_list: post_value.append(file_item['filename']) else: post_value = request.get_argument(key, None) print post_value # 让提交的数据 和 定义的正则表达式进行匹配 field_obj.match(key, post_value) if field_obj.id_valid: self.value_dict[key] = field_obj.value else: self.error_dict[key] = field_obj.error self.valid_status = False class ListForm(object): def __init__(self, form_type): self.form_type = form_type self.valid_status = True self.value_dict = {} self.error_dict = {} def validate(self, request): name_list = request.request.arguments.keys() + request.request.files.keys() index = 0 flag = False while True: pre_key = "[%d]" % index for name in name_list: if name.startswith(pre_key): flag = True break if flag: form_obj = self.form_type() form_obj.validate(request, depth=10, pre_key="[%d]" % index) if form_obj.valid_status: self.value_dict[index] = form_obj.value_dict else: self.error_dict[index] = form_obj.error_dict self.valid_status = False else: break index += 1 flag = False class MainForm(Form): def __init__(self): # self.ip = IPField(required=True) # self.port = IntegerField(required=True) # self.new_ip = IPField(required=True) # self.second = SecondForm() self.fff = FileField(required=True) super(MainForm, self).__init__() # # class SecondForm(Form): # # def __init__(self): # self.ip = IPField(required=True) # self.new_ip = IPField(required=True) # # super(SecondForm, self).__init__() class MainHandler(tornado.web.RequestHandler): def get(self): self.render('index.html') def post(self, *args, **kwargs): # for i in dir(self.request): # print i # print self.request.arguments # print self.request.files # print self.request.query # name_list = self.request.arguments.keys() + self.request.files.keys() # print name_list # list_form = ListForm(MainForm) # list_form.validate(self) # # print list_form.valid_status # print list_form.value_dict # print list_form.error_dict # obj = MainForm() # obj.validate(self) # # print "验证结果:", obj.valid_status # print "符合验证结果:", obj.value_dict # print "错误信息:" # for key, item in obj.error_dict.items(): # print key,item # print self.get_arguments('favor'),type(self.get_arguments('favor')) # print self.get_argument('favor'),type(self.get_argument('favor')) # print type(self.get_argument('fff')),self.get_argument('fff') # print self.request.files # obj = MainForm() # obj.validate(self) # print obj.valid_status # print obj.value_dict # print obj.error_dict # print self.request,type(self.request) # obj.fff.save(self.request) # from tornado.httputil import HTTPServerRequest # name_list = self.request.arguments.keys() + self.request.files.keys() # print name_list # print self.request.files,type(self.request.files) # print len(self.request.files.get('fff')) # obj = MainForm() # obj.validate(self) # print obj.valid_status # print obj.value_dict # print obj.error_dict # obj.fff.save(self.request) self.write('ok') settings = { 'template_path': 'template', 'static_path': 'static', 'static_url_prefix': '/static/', 'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh', 'login_url': '/login' } application = tornado.web.Application([ (r"/index", MainHandler), ], **settings) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()

浙公网安备 33010602011771号