0%

Tornado框架

Tornado是一个Python Web框架和异步网络库,起初由FriendFeed开发,通过使用非阻塞网络I/OTornado可以支撑上万级的连接,处理长连接,WebSockets和其他需要与每个用户保持长久连接的应用。

编写第一个Tornado程序

首先创建Tornado工程,tornado_demo,在该工程下创建一个hello.py文件。

输入以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import tornado.ioloop  # 主事件循环模块
import tornado.web # Web框架模块


class MainHandler(tornado.web.RequestHandler):
"""定义get方法用于接收GET请求"""
def get(self):
self.write('Hello world!') # 输出字符串

def make_app():
"""创建tornado应用,设置相关路由信息"""
return tornado.web.Application([
(r"/", MainHandler), # 设置路由
],
debug=True)

if __name__ == '__main__':
app = make_app() # 创建Tornado应用
app.listen(8888) # 设置监听端口
print("Starting Server on port 8888...") # 输出提示信息
tornado.ioloop.IOLoop.current().start() # 启动服务

然后在cmd控制台中运行该脚本。

image-20220815094705746

可以看到该Tornado项目已经运行起来了。

在浏览器中输入http://127.0.0.1:8888/即可访问Web界面。

image-20220813154517337

这样我们的第一个Tornado项目就创建好了,在指定端口下可以访问当index页面。

其中:

  • ioloop是主事件循环模块。
  • web是WEB框架模块
  • MainHandler类用于处理用户请求。
  • make_app函数,用于创建Tornado应用,并设置路由信息。debug=True表示进入调试模式,每次修改代码后会重启服务。在创建好应用后,还需要指定监听的端口,调用其listen()方法。

设置多个路由

前面我们已经创建了一个简单的Tornado项目,在网页根目录上输出Hello world

能够输出该内容是因为我们在make_app函数中设置了路由对应规则,将根目录'/',对应到了MainHandler类,在访问根目录的时候就会调用MainHandler类中的get方法给用户返回相应的页面内容。

但是一个Web项目不可能只有单个页面,当出现多个页面的时候如何进行处理呢?这个时候就需要设置多条路由规则,将不用的页面内容对应到不同的Handler类,处理不同的任务。

例如,我们再定义一个/index路由规则。

此时只需要做两个改动。

  1. 增加一个IndexHandler类。
1
2
3
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.write('Hello Index')
  1. make_app中设置路由
1
2
3
4
5
6
7
def make_app():
"""创建tornado应用,设置相关路由信息"""
return tornado.web.Application([
(r"/", MainHandler), # 设置路由
(r"/index", IndexHandler)
],
debug=True)
  • 访问:http://127.0.0.1:8888/index

image-20220813164134853

同时路由规则还支持使用正则表达式进行匹配。

  • 修改路由让路径http://127.0.0.1:8888/re/REahttp://127.0.0.1:8888/re/REbhttp://127.0.0.1:8888/re/REchttp://127.0.0.1:8888/re/REd均返回MainHandler

    修改make_app代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    def make_app():
    """创建tornado应用,设置相关路由信息"""
    return tornado.web.Application([
    (r"/", MainHandler), # 设置路由
    (r"/index", IndexHandler),
    (r"/re/RE[abcd]", MainHandler)
    ],
    debug=True)
  • 访问http://127.0.0.1:8888/re/REahttp://127.0.0.1:8888/re/REbhttp://127.0.0.1:8888/re/REchttp://127.0.0.1:8888/re/REd发现均返回以下页面。

image-20220813172121232

HTTP方法

Tornado同样支持各种请求,请求需要在Handler中进行定义,继承自tornado.web.RequestHander父类。

RequestHander提供了如下方法:

  • RequestHander.get(*args, **kwargs)
  • RequestHander.head(*args, **kwargs)
  • RequestHander.post(*args, **kwargs)
  • RequestHander.delete(*args, **kwargs)
  • RequestHander.patch(*args, **kwargs)
  • RequestHander.put(*args, **kwargs)
  • RequestHander.options(*args, **kwargs)

所以,只需要在自定义的操作类中创建对应方法,即可实现增删改查等功能。

例如:创建一个既可以接收GET请求,又可以接收POST请求的LoginHander类,代码如下:

1
2
3
4
5
6
7
8
class LoginHander(tornado.web.RequestHandler):
def get(self):
self.write("This is login page")

def post(self):
username = self.get_argument('username', '') # 接收用户名参数
password = self.get_argument('password', '') # 接收密码参数
self.write(f'username is {username} password is {password}')

make_app中添加路由信息:

1
2
3
4
5
6
7
8
def make_app():
"""创建tornado应用,设置相关路由信息"""
return tornado.web.Application([
(r"/", MainHandler), # 设置路由
(r"/index", IndexHandler),
(r"/login", LoginHander) # 设置登录路由
],
debug=True)
  • 访问http://127.0.0.1:8888/login

image-20220813164919289

可以发现get请求成功

然后打开一个cmd终端,测试post请求,输入以下代码:

1
curl -d "username=minglog&password=123456" "http://127.0.0.1:8888/login" 

返回结果:

image-20220813165203770

发现post请求成功

模板

前面我们响应的结果都是简单的文字,在实际需求中仅仅返回文字内容肯定是不够的,我们一般会返回相对应的HTML页面内容,返回的HTML页面内容我们称为模板文件。

使用模板时,需要先在应用中设置template_path模板路径【在make_app函数中设置template_path的值,一般设置为当前工程路径下的templates文件夹】,然后使用render()方法渲染模板。

设置模板路径

1
2
3
4
5
6
7
8
9
10
def make_app():
"""创建tornado应用,设置相关路由信息"""
return tornado.web.Application([
(r"/", MainHandler), # 设置路由
(r"/index", IndexHandler),
(r"/login", LoginHander) # 设置登录路由
],
template_path = os.path.join(os.path.dirname(__file__), "templates"), # 设置模板路径
debug=True # 调试模式
)

创建templates文件夹,并编写模板文件

模板文件代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<form class="form-horizontal" action="" method="post">
<div class=""form-group>
<label for="username" class="col-sm-2 control-label">用户名</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="username" name="username" placeholder="请输入用户名">
</div>
</div>
<div class=""form-group>
<label for="password" class="col-sm-2 control-label">密 码</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="password" name="password" placeholder="请输入密码">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary"> 登录</button>
</div>
</div>
</form>
</body>
</html>

修改LoginHander

为了让响应返回指定的模板文件,需要对前面的LoginHander类中的get方法进行相应的修改,让其返回login.html页面。

1
2
3
4
5
6
7
8
class LoginHander(tornado.web.RequestHandler):
def get(self):
self.render("login.html")

def post(self):
username = self.get_argument('username', '') # 接收用户名参数
password = self.get_argument('password', '') # 接收密码参数
self.write(f'username is {username} password is {password}')

测试结果

  • 再次访问http://127.0.0.1:8888/login

image-20220813171000575

  • 输入用户名和密码,点击登录。

image-20220813171029949

发现可以直接返回我们输入的内容。

-------------本文结束感谢您的阅读-------------