我使用Tornado创建一个可以在同步方法上工作的登录页面。现在我想让它异步。那么我应该对以下代码做些什么改变:
import tornado.ioloop
import tornado.web
import http
import time
class BaseHandler(tornado.web.RequestHandler):
def get_current_user(self):
return self.get_secure_cookie("user")
class MainHandler(BaseHandler):
def get(self):
if not self.current_user:
self.redirect("/login")
return
name = tornado.escape.xhtml_escape(self.current_user)
self.write('<html>'
'<head> '
'<title>WELCOME </title>'
'</head>'
'<body style="background:orange;"'
'<div align="center">'
'<div style="margin-top:200px;margin-bottom:10px;">'
'<span style="width:500px;color:black;font-size:30px;font-weight:bold;">WELCOME </span>'
'</div>'
'<div style="margin-bottom:5px;">'"Hello, " + name)
class LoginHandler(BaseHandler):
def get(self):
self.write('<html><body><form action="/login" method="post">'
'<div><span style="width:100px;;height: 500px;color:black;font-size:60;font-weight:bold;">'
'LOGIN'
'<div><span style="width:100px;;height: 500px;color:purple;font-size:30;font-weight:bold;">'
'NAME <input type="text" name="name">'
'<div><span style="width:100px;height: 500px;color:purple;font-size:30;font-weight:bold;">PASSWORD</span><input style="width:150px;" type="password" name="password" id="password" value="">'
'</div>'
'<input type="submit" value="Sign in">'
'</form></body></html>')
def post(self):
self.set_secure_cookie("user", self.get_argument("name"))
time.sleep(10)
self.redirect("/")
application = tornado.web.Application([
(r"/", MainHandler),
(r"/login", LoginHandler),
], cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__")
application.listen(5000)
tornado.ioloop.IOLoop.current().start()
在我的代码中,我有三个类BaseHandler
,MainHandler
和LoginHandler
。任何帮助将不胜感激。
答案 0 :(得分:1)
Python 3.5 引入了 async
和 await
关键字(使用这些关键字的函数也称为“原生协程”)。有关详情,请参阅 the answer。
我们为 Tornado 应用构建的每个基于类的视图都必须从 RequestHandler
中找到的 tornado.web
对象继承。
如果我们重写 prepare
方法,我们可以设置一些逻辑来运行,每当收到 request
时都会执行。
def tornado.web.RequestHandler.get_current_user(self)
- 覆盖以确定当前用户,例如,cookie。
子类可以覆盖 get_current_user()
,它会在第一次访问 self.current_user
时自动调用。 get_current_user()
每次请求只会被调用一次,并被缓存以备将来访问。
class BaseHandler(tornado.web.RequestHandler):
def prepare(self):
if self.get_argument("btn1", None) is not None:
print("detected click on btn Profile")
self.redirect("/profile")
return
if self.get_argument("btn2", None) is not None:
print("detected click on btn Sources")
self.redirect("/sources")
return
if self.get_argument("logout", None) is not None:
self.clear_cookie("username")
self.redirect("/")
return
if self.get_argument("btnSignIn", None) is not None:
print("detected click on btnSignIn")
self.redirect("/signin")
return
def get_current_user(self):
a = self.get_secure_cookie("username")
if a:
return a.decode("utf-8")
return None
class LoginHandler(BaseHandler):
def get(self):
self.render('login.html')
def prepare(self):
super().prepare()
async def post(self):
username = self.get_argument("username")
password = self.get_argument("password")
my_db = self.settings['db']
collection = my_db.test
val = await do_check_one(collection, username, password)
if val is not None:
self.set_secure_cookie("username", username)
self.redirect("/profile")
else:
self.write('<h1>Wrong credentials</h1>')
为了更好地理解,请参阅:Simplest async/await example。
async def do_check_one(my_collection, value1, value2):
document = await my_collection.find_one({"name": value1, "password": value2})
return document
和main
:
选项在使用前必须用 tornado.options.define
定义,一般在模块的顶层。然后这些选项可以作为 tornado.options.options
的属性访问。
您的应用程序的 main()
方法不需要知道整个程序中使用的所有选项;它们都是在加载模块时自动加载的。但是,必须在解析命令行之前导入所有定义选项的模块。
define("port", default=8000, help="run on the given port", type=int)
if __name__ == '__main__':
tornado.options.parse_command_line()
client = motor.motor_tornado.MotorClient('localhost', 27017)
db = client.test
collection = db.test
settings = {
"template_path": os.path.join(os.path.dirname(__file__), "templates"),
"static_path": os.path.join(os.path.dirname(__file__), "static"),
"cookie_secret": "_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
"login_url": "/login",
"db": db,
"debug": True,
"xsrf_cookies": True
}
app = tornado.web.Application(
handlers=[(r'/', MainHandler),
(r'/sources', SourceHandler),
(r'/login', LoginHandler),
(r'/signin', SigninHandler),
(r'/profile', ProfileHandler),
(r'/favicon.ico', tornado.web.StaticFileHandler, dict(path=settings['static_path'])),
],
**settings
)
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
try:
tornado.ioloop.IOLoop.instance().start()
print('Server started...')
except KeyboardInterrupt:
print('Server has shut down.')
您的 main()
方法可以解析命令行或使用 parse_command_line
或 parse_config_file
解析配置文件:
tornado.options.parse_command_line()
# or
tornado.options.parse_config_file("/etc/server.conf")
答案 1 :(得分:0)
在此处使用async/await
表示法,可能适合您的Python版本:
class BaseHandler(tornado.web.RequestHandler):
async def get_current_user(self):
return await some_async_operation()
async def prepare(self):
self.current_user = await self.get_current_user()
Tornado没有开箱即用地异步调用get_current_user
,但它会使用异步prepare
方法,因此您可以填充self.current_user
。
答案 2 :(得分:0)
不要打电话&#34;睡觉&#34;在龙卷风方法中:
http://www.tornadoweb.org/en/stable/faq.html
如果您希望暂停该方法一段时间以证明自己没有阻止,请添加from tornado import gen
并尝试:
async def post(self):
self.set_secure_cookie("user", self.get_argument("name"))
yield gen.sleep(10)
self.redirect("/")
或者在Python 2中:
@gen.coroutine
def post(self):
self.set_secure_cookie("user", self.get_argument("name"))
yield gen.sleep(10)
self.redirect("/")