Tornado-请求与响应
整理基础工程
目录层级
1 | project # 工程总目录 |
以后写新项目直接拷贝基础工程即可!
基础工程
-
ORM
__init__.py
:空文件即可。orm.py
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
29
30
31
32
33
34
35
36
37
38
39
40
41from .sunckMysql import SunckMySQL
class ORM():
def save(self):
# insert into students (name,age) values('tyui',32)
tableName = (self.__class__.__name__).lower()
fieldsStr = valuesStr = "("
for field in self.__dict__:
fieldsStr += (field + ",")
if isinstance(self.__dict__[field], str):
valuesStr += ("'" + self.__dict__[field] + "',")
else:
valuesStr += (str(self.__dict__[field]) + ",")
fieldsStr = fieldsStr[:len(fieldsStr) - 1] + ")"
valuesStr = valuesStr[:len(valuesStr) - 1] + ")"
sql = "insert into " + tableName + " " + fieldsStr + " values " + valuesStr
# print(sql)
db = SunckMySQL()
# db2 = SunckMySQL()
# print(db is db2)
db.insert(sql)
def delete(self):
pass
def update(self):
pass
def all(cls):
# select * from students
tableName = (cls.__name__).lower()
sql = "select * from " + tableName
db = SunckMySQL()
# print(sql)
return db.get_all_obj(sql, tableName)
def filter(cls):
passsunckMysql.py
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96import pymysql
import config
def singleton(cls, *args, **kwargs):
instances = {}
def _singleton():
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return _singleton
class SunckMySQL():
host = config.mysql["host"]
user = config.mysql["user"]
passwd = config.mysql["passwd"]
dbName = config.mysql["dbName"]
def connet(self):
self.db = pymysql.connect(self.host, self.user, self.passwd, self.dbName)
self.cursor = self.db.cursor()
def close(self):
self.cursor.close()
self.db.close()
def get_one(self, sql):
res = None
try:
self.connet()
self.cursor.execute(sql)
res = self.cursor.fetchone()
self.close()
except:
print("查询失败")
return res
def get_all(self, sql):
res = ()
try:
self.connet()
self.cursor.execute(sql)
res = self.cursor.fetchall()
self.close()
except:
print("查询失败")
return res
def get_all_obj(self, sql, tableName, *args):
resList = []
fieldsList = []
if (len(args) > 0):
for item in args:
fieldsList.append(item)
else:
fieldsSql = "select COLUMN_NAME from information_schema.COLUMNS where table_name = '%s' and table_schema = '%s'" % (
tableName, self.dbName)
fields = self.get_all(fieldsSql)
for item in fields:
fieldsList.append(item[0])
# 执行查询数据sql
res = self.get_all(sql)
for item in res:
obj = {}
count = 0
for x in item:
obj[fieldsList[count]] = x
count += 1
resList.append(obj)
return resList
def insert(self, sql):
return self.__edit(sql)
def update(self, sql):
return self.__edit(sql)
def delete(self, sql):
return self.__edit(sql)
def __edit(self, sql):
count = 0
try:
self.connet()
count = self.cursor.execute(sql)
self.db.commit()
self.close()
except:
print("事物提交失败")
self.db.rollback()
return count
-
views
__init__.py
:空文件即可。index.py
1
2
3
4
5
6
7
8import tornado.web
from tornado.web import RequestHandler
class StaticFileHandler(tornado.web.StaticFileHandler):
def __init__(self, *args, **kwargs):
super(StaticFileHandler, self).__init__(*args, **kwargs)
self.xsrf_token
-
application.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14import os
import config
import tornado.web
from views import index
class Application(tornado.web.Application):
def __init__(self):
handlers = [
# 静态页面路由,其它路由写在该路由上边
(r'/(.*)$', index.StaticFileHandler,
{'path': os.path.join(config.BASE_DIRS, 'static/html'), 'default_filename': 'index.html'})
]
super(Application, self).__init__(handlers, **config.settings) -
config.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25import os
BASE_DIRS = os.path.dirname(__file__)
# 参数
options = {
'port': 8000,
}
# 数据库配置
mysql = {
'host': 'IP',
'user': '用户名',
'passwd': '密码',
'dbName': '数据库名'
}
# 配置
settings = {
'debug': True,
'xsrf_cookies': True,
'static_path': os.path.join(BASE_DIRS, 'static'),
'template_path': os.path.join(BASE_DIRS, 'templates'),
'cookie_secret': 'wtsaTrAfTBuZTx5f9yBhX8ZVZ479HknqnSMKKAmau+0=',
} -
models.py
1
2
3
4
5
6
7from ORM.orm import ORM
'''
class ClassName(ORM):
def __init__(self):
pass
''' -
server.py
1
2
3
4
5
6
7
8
9
10
11
12import config
import tornado.ioloop
import tornado.httpserver
from application import Application
if __name__ == '__main__':
app = Application()
httpServer = tornado.httpserver.HTTPServer(app)
httpServer.bind(config.options['port'])
httpServer.start(1)
tornado.ioloop.IOLoop.current().start() -
static -> html -> index.html
1
2
3
4
5
6
7
8
9
10
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Tornado</title>
</head>
<body>
<h1 style="text-align: center">Welcome to Tornado!</h1>
</body>
</html> -
⚠️注意:我没写的文件或目录即为空文件或目录,直接创建即可!
Application
settings
debug
:- 作用:设置tornado是否工作在调试模式下,默认为
False
,即工作在生产模式下。 True
的特性- 自动重启:
- tornado应用会监控源代码文件,当有保存改动时便会重启服务器,可以减少手动重启的次数,提高开发效率。
- 如果保存后代码有错误会导致重启失败,修改错误后需要手动重启。
- 可以通过
autoreload = True
设置
- 取消缓存编译的模板:可以通过
compiled_template_cache = False
单独设置 - 取消缓存静态文件的hash值:可以通过
static_hash_cache = False
单独设置 - 提供追踪信息:可以通过
server_traceback = True
单独设置
- 自动重启:
- 作用:设置tornado是否工作在调试模式下,默认为
static_path
:设置静态文件目录template_path
:设置模板文件目录autoescape
:当未None时关闭当前项目的自动转义,一般不建议使用cookie_secret
:配置安全cookie秘钥xsrf_cookies
:当为True开启XSRF保护login_url
:用户验证失败会映射该路由- 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import os
BASE_DIRS = os.path.dirname(__file__)
# 参数
options = {
'port': 9000,
}
# 配置
settings = {
'debug': False,
'xsrf_cookies': True,
'login_url': '/login',
'static_path': os.path.join(BASE_DIRS, 'static'),
'template_path': os.path.join(BASE_DIRS, 'templates'),
'cookie_secret': 'wtsaTrAfTBuZTx5f9yBhX8ZVZ479HknqnSMKKAmau+0=',
}
路由
(r'/', index.IndexHandler)
(r'/sunck', index.SunckHandler, {'word1': 'nice', 'word2': 'handsome'})
1
2
3
4
5
6
7
8
9class SunckHandler(RequestHandler):
# 该方法会在HTTP方法之前调用
def initialize(self, word1, word2):
self.word1 = word1
self.word2 = word2
def get(self, *args, **kwargs):
print(self.word1, self.word2)
self.write('Sunck is a good man!')⚠️注意:需要重写
initialize
方法接收传递的参数tornado.web.url(r'/kaige', index.KaigeHandler, {"word3": "handsome", "word4": "cool"}, name="index")
1
2
3
4
5
6
7
8class KaigeHandler(RequestHandler):
def initialize(self, word3, word4):
self.word3 = word3
self.word4 = word4
def get(self, *args, **kwargs):
print(self.word3, self.word4)
self.write("kaige is a nice man")⚠️注意:如果使用
name
属性,不能使用元组路由,需要使用tornado.web.url
定义路由- 反向解析
1
tornado.web.url(r'/home', index.KaigeHandler, {"word3": "handsome", "word4": "cool"}, name="kaigegood")
1
2
3
4
5class IndexHandler(RequestHandler):
def get(self, *args, **kwargs):
self.write('Hello world!')
url = self.reverse_url('kaigegood')
self.write("<a href='%s'>去另一个界面</a>" % (url))self.reverse_url('kaigegood')
:会获取到name
为"kaigegood"
的路由的正则匹配 - 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import tornado.web
from views import index
import config
class Application(tornado.web.Application):
def __init__(self):
handlers = [
(r'/', index.IndexHandler),
(r'/home', index.HomeHandler),
(r'/sunck', index.SunckHandler, {'word1': 'nice', 'word2': 'handsome'}),
# tornado.web.url(r'/kaige', index.KaigeHandler, {"word3": "handsome", "word4": "cool"}, name="index"),
tornado.web.url(r'/home', index.KaigeHandler, {"word3": "handsome", "word4": "cool"}, name="kaigegood"),
]
super(Application, self).__init__(handlers, **config.settings)application.py
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
29
30
31
32
33
34
35import tornado.web
from tornado.web import RequestHandler
class IndexHandler(RequestHandler):
def get(self, *args, **kwargs):
self.write('Hello world!')
url = self.reverse_url('kaigegood')
self.write("<a href='%s'>去另一个界面</a>" % (url))
class HomeHandler(RequestHandler):
def get(self, *args, **kwargs):
self.write('Welcome to home!')
class SunckHandler(RequestHandler):
# 该方法会在HTTP方法之前调用
def initialize(self, word1, word2):
self.word1 = word1
self.word2 = word2
def get(self, *args, **kwargs):
print(self.word1, self.word2)
self.write('Sunck is a good man!')
class KaigeHandler(RequestHandler):
def initialize(self, word3, word4):
self.word3 = word3
self.word4 = word4
def get(self, *args, **kwargs):
print(self.word3, self.word4)
self.write("kaige is a nice man")index.py
tornado.web.RequestHandler
利用HTTP协议向服务器传递参数
-
提取URI的特定部分:http://127.0.0.1:8000/liuyifei/good/nice/handsom
-
(r'/liuyifei/(\w+)/(\w+)/(\w+)', index.LiuyifeiHandler)
1
2
3
4class LiuyifeiHandler(RequestHandler):
def get(self, p1, p2, p3, *args, **kwargs):
print(p1 + "-" + p2 + "-" + p3)
self.write("liuyifei is a nice women") -
(r'/liuyifei/(?P<p1>\w+)/(?P<p3>\w+)/(?P<p2>\w+)', index.LiuyifeiHandler)
1
2
3
4class LiuyifeiHandler(RequestHandler):
def get(self, p1, p2, p3, *args, **kwargs):
print(p1 + "-" + p2 + "-" + p3)
self.write("liuyifei is a nice women")
-
-
get方式传递参数
-
http://127.0.0.1:8000/zhangmanyu?a=1&b=2&c=3
self.get_query_argument(name, default=ARG_DEFAULT, strip=True)
- 参数
name
- 从get请求参数字符串中返回指定参数的值
- 如果出现过个同名参数,返回最后一个值
default
- 设置未传的name参数时返回默认的值,如果default也没有设置,会抛出
tornado.web.MissingArgumentError
异常
- 设置未传的name参数时返回默认的值,如果default也没有设置,会抛出
strip
- 表示是否过滤掉左右两边的空白字符,默认为True过滤
- 示例
1
2
3
4
5
6
7class ZhangmanyuHandler(RequestHandler):
def get(self, *args, **kwargs):
a = self.get_query_argument("a")
b = self.get_query_argument("b")
c = self.get_query_argument("c", strip=False)
print(a, b, "*" + c + "*")
self.write("zhangmanyu is a good women")
-
http://127.0.0.1:8000/zhangmanyu?a=1&a=2
self.get_query_arguments(name, strip=True)
- 参数:同上
- 示例
1
2
3
4
5class ZhangmanyuHandler(RequestHandler):
def get(self, *args, **kwargs):
alist = self.get_query_arguments("a")
print(alist[0], alist[1])
self.write("zhangmanyu is a good women")
-
-
post方式传递参数
self.get_body_argument(name, default=ARG_DEFAULT, strip=True)
self.get_body_arguments(name, strip=True)
- 示例:
(r'/postfile', index.PostFileHandler)
1
2
3
4
5
6
7
8
9
10class PostFileHandler(RequestHandler):
def get(self, *args, **kwargs):
self.render('postfile.html')
def post(self, *args, **kwargs):
name = self.get_body_argument("username")
passwd = self.get_body_argument("passwd")
hobbyList = self.get_body_arguments("hobby")
print(name, passwd, hobbyList)
self.write("sunck is a handsome man")index.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/postfile" method="post">
姓名:<input type="text" name="username"/>
<hr/>
密码:<input type="password" name="passwd"/>
<hr/>
爱好:
<input type="checkbox" value="power" name="hobby">权利
<input type="checkbox" value="money" name="hobby">金钱
<input type="checkbox" value="book" name="hobby">书
<input type="submit" value="登陆"/>
</form>
</body>
</html>templates -> postfile.html
-
即可以获取get请求,也可以获取post请求
self.get_argument(name, default=ARG_DEFAULT, strip=True)
self.get_argument(name, strip=True)
- ⚠️注意:一般不会选用
-
在http报文的头中增加自定义的字段
request
对象
- 作用:存储了关于请求的相关信息
- 属性:http://127.0.0.1:8000/zhuyin?a=1&b=2
method
:HTTP请求的方式host
:被请求的主机名uri
:请求的完整资源地址,包括路径和get查询参数部分,如:/zhuyin?a=1&b=2
path
:请求的路径部分,如:/zhuyin
query
:请求参数部分,如:a=1&b=2
version
:使用的HTTP版本,如:HTTP/1.1
headers
:请求的协议头,是一个字典类型body
:请求体数据remote_ip
:客户端的ipfiles
:用户上传的文件,字典类型1
2
3
4
5
6
7
8
9{
'file': [
{
'filename': 'a.txt',
'body': b'sunck is a good man',
'content_type': 'text/plain'
}
]
}1
2
3
4
5
6
7
8
9
10
11
12
13
14{
'file': [
{
'filename': 'a.txt',
'body': b'sunck is a good man',
'content_type': 'text/plain'
},
{
'filename': 'b.txt',
'body': b'sunck is a nice man',
'content_type': 'text/plain'
}
]
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21{
'file': [
{
'filename': 'a.txt',
'body': b'sunck is a good man',
'content_type': 'text/plain'
},
{
'filename': 'b.txt',
'body': b'sunck is a nice man',
'content_type': 'text/plain'
}
],
'img': [
{
'filename': 'tornado高效原理图.png',
'body': b'qwertyuiosdghj',
'content_type': 'image/png'
}
]
}
- 示例:
(r'/zhuyin', index.ZhuyinHandler)
1
2
3
4
5
6
7
8
9
10
11
12
13class ZhuyinHandler(RequestHandler):
def get(self, *args, **kwargs):
print(self.request.method)
print(self.request.host)
print(self.request.uri)
print(self.request.path)
print(self.request.query)
print(self.request.version)
print(self.request.headers)
print(self.request.body)
print(self.request.remote_ip)
print(self.request.files)
self.write("zhuyin is a good women")
tornador.httputil.HTTPFile
对象
- 作用:是接收到的文件对象
- 属性
filename
:文件的实际名字body
:文件的数据实体content_type
:文件的类型
- 示例:
(r'/linqingxia', index.UpFileHandler)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import os
import config
class UpFileHandler(RequestHandler):
def get(self, *args, **kwargs):
self.render('upfile.html')
def post(self, *args, **kwargs):
filesDict = self.request.files
for inputname in filesDict:
fileArr = filesDict[inputname]
for fileObj in fileArr:
# 存储路径
filePath = os.path.join(config.BASE_DIRS, 'upfile/' + fileObj.filename)
with open(filePath, "wb") as f:
f.write(fileObj.body)
self.write("ok")index.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上传文件</title>
</head>
<body>
<form method="post" action="/linqingxia" enctype="multipart/form-data">
<input type="file" name="file"/>
<input type="file" name="file"/>
<input type="file" name="img"/>
<input type="submit" value="上传"/>
</form>
</body>
</html>templates -> upfile.html
响应输出
write
- 原型:
self.write(chunk)
- 作用:将
chunk
数据写到输出缓冲区 - 图示
- 基础:
(r'/write', index.WriteHandler)
1
2
3
4
5
6
7
8
9class WriteHandler(RequestHandler):
def get(self, *args, **kwargs):
self.write("sunck is a good man")
self.write("sunck is a nice man")
self.write("sunck is a handsome man")
# 刷新缓冲区,关闭当次请求通道
self.finish()
# 在finish下边就不要再write
# self.write("sunck is a cool man") - 利用write方法写json数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14import json
class Json1Handler(RequestHandler):
def get(self, *args, **kwargs):
per = {
"name": "sunck",
"age": 18,
"height": 175,
"weight": 70
}
# 将字典转换成json字符串
jsonStr = json.dumps(per)
self.write(jsonStr)1
2
3
4
5
6
7
8
9class Json2Handler(RequestHandler):
def get(self, *args, **kwargs):
per = {
"name": "kaige",
"age": 18,
"height": 175,
"weight": 70
}
self.write(per)⚠️注意:自己手动序列化Json方式
Content-Type
属性值为text/html
,而采用write自动序列化方式,Content-Type
属性为application/json
self.set_header(name, value)
- 作用:手动设置一个名为name,值为value的响应头字段
- 参数
name
:字段名称value
:字段值
- 示例:
(r'/json1', index.Json1Handler)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import json
class Json1Handler(RequestHandler):
def get(self, *args, **kwargs):
per = {
"name": "sunck",
"age": 18,
"height": 175,
"weight": 70
}
# 将字典转换成json字符串
jsonStr = json.dumps(per)
self.set_header("Content-Type", "application/json; charset=UTF-8")
self.set_header("sunck", "good")
self.write(jsonStr)
set_default_headers()
- 作用:在进入HTTP响应处理方法之前被调用,可以重写该方法来预先设置默认的headers
- ⚠️注意:在HTTP处理方法中使用
set_header
设置的字段会覆盖set_default_headers()
里设置的默认字段的值 - 示例:
(r'/header', index.HeaderHandler)
1
2
3
4
5
6
7
8
9
10
11class HeaderHandler(RequestHandler):
def set_default_headers(self):
self.set_header("Content-Type", "text/html; charset=UTF-8")
self.set_header("kaige", "nice")
def get(self, *args, **kwargs):
self.set_header("kaige", "handsome")
self.write("good nice")
def post(self, *args, **kwargs):
pass
self.set_status(status_code, reason=None)
- 作用:为响应设置状态码
- 参数:
status_code
:- 状态码值,为
int
类型 - 如果
reason
的值为None
,则状态码必须为正常值
- 状态码值,为
reason
:描述状态码的词组,string
类型
- 示例:
(r'/status', index.StatusCodeHandler)
1
2
3
4class StatusCodeHandler(RequestHandler):
def get(self, *args, **kwargs):
self.set_status(404)
self.write("************************")1
2
3
4class StatusCodeHandler(RequestHandler):
def get(self, *args, **kwargs):
self.set_status(999, "who?where?what?")
self.write("************************")1
2
3
4
5class StatusCodeHandler(RequestHandler):
def get(self, *args, **kwargs):
self.write("************************")
# 会报错
self.set_status(999)
self.redirect(url)
:重定向
- 作用:重定向到url网址
- 示例:
(r'/index', index.RedirectHandler)
1
2
3class RedirectHandler(RequestHandler):
def get(self, *args, **kwargs):
self.redirect("/")
self.send_error(status_code=500, **kwargs)
- 作用:抛出HTTP错误状态码,默认为500,抛出错误后tornado会调用
wirte_error()
方法进行处理,并返回给浏览器错误界面 - ⚠️注意:在
send_error
之下就不要在响应输出了
wirte_error(status_code, **kwargs)
- 作用:用来处理
send_error
抛出的错误信息,并返回给浏览器错误界面 - 示例:
(r'/iserror', index.ErrorHandler)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class ErrorHandler(RequestHandler):
def write_error(self, status_code, **kwargs):
if status_code == 500:
code = 500
# 返回500界面
self.write("服务器内部错误")
elif status_code == 404:
code = 404
# 返回404界面
self.write("资源不存在")
self.set_status(code)
def get(self, *args, **kwargs):
flag = self.get_query_argument("flag")
print(type(flag))
if flag == '0':
self.send_error(404)
self.write("you are right")