问题
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