Asynchronous login tornado

寵の児 提交于 2021-02-16 19:02:35

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!