问题
I used Tornado to create a login page which works on synchronous method.Now I want to make it asynchronous. So what are the changes I should do to the following code:
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()
In my code I have three classes BaseHandler, MainHandler and LoginHandler. Any help will be appreciated.
回答1:
Don't call "sleep" in a Tornado method:
http://www.tornadoweb.org/en/stable/faq.html
If you want to pause the method for a while to prove to yourself that it's non-blocking, add from tornado import gen and try:
async def post(self):
self.set_secure_cookie("user", self.get_argument("name"))
yield gen.sleep(10)
self.redirect("/")
Or in Python 2:
@gen.coroutine
def post(self):
self.set_secure_cookie("user", self.get_argument("name"))
yield gen.sleep(10)
self.redirect("/")
回答2:
Using async/await notation here, possibly adapt to fit your Python version:
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 won't call get_current_user asynchronously out of the box, but it will honour asynchronous prepare methods, so you can populate self.current_user there.
回答3:
A simple example
Python 3.5 introduced the async and await keywords (functions using these keywords are also called “native coroutines”). See the answer for more details.
Every class-based view we construct for our Tornado app must inherit from the RequestHandler object found in tornado.web.
If we override the prepare method, we can set some logic to run that'll do whenever a request is received.
def tornado.web.RequestHandler.get_current_user(self) - Override to determine the current user from, e.g., a cookie.
A subclass may override get_current_user(), which will be called automatically the first time self.current_user is accessed. get_current_user() will only be called once per request, and is cached for future access.
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>')
For a better understanding see: 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
and main:
Options must be defined with tornado.options.define before use, generally at the top level of a module. The options are then accessible as attributes of tornado.options.options.
The main() method of your application does not need to be aware of all of the options used throughout your program; they are all automatically loaded when the modules are loaded. However, all modules that define options must have been imported before the command line is parsed.
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.')
Your main() method can parse the command line or parse a config file with either parse_command_line or parse_config_file:
tornado.options.parse_command_line()
# or
tornado.options.parse_config_file("/etc/server.conf")
来源:https://stackoverflow.com/questions/45408840/asynchronous-login-tornado