02 | tornado的web基础
tornado之helloworld
一个简单的tornado web应用程序我们要继承tornado web模块中的 RequestHandler ,内部重构请求方法。
然后实例化应用对象,添加映射关系,监听端口,开启时间循环
from tornado import web import tornado class MainHandler(web.RequestHandler): # 当客户端发起不同的http方法的时候, 只需要重载handler中的对应的方法即可 # 下面的get方法,响应http请求中的get请求,请求类型与对应的方法如下: # "GET":get(), "HEAD":head(), "POST":post(), "DELETE":delete(), "PATCH":patch(), "PUT":put(),"OPTIONS":options() async def get(self, *args, **kwargs): self.write("hello world") # 二、程序运行入口 if __name__ == "__main__": # 1、实例化,application对象 app = web.Application([ ("/", MainHandler), # 配置路由 http://localhost:8888/这个url交给MainHandler处理 ], debug=True # 开启调试模式,与flask、Django类似,可以自动重启,打印错误栈 ) app.listen(8888) tornado.ioloop.IOLoop.current().start()
开启了调试模式 debug = true后,修改代码python会开启一个线程在后台运行,可以通过资源管理器杀死这个线程
访问
http://127.0.0.1:8888/
返回结果如下
tornado中为什么不能写同步的方法
如下示例代码:
import time from tornado import web import tornado web.URLSpec class MainHandler(web.RequestHandler): async def get(self, *args, **kwargs): time.sleep(5) self.write("hello world") class MainHandler2(web.RequestHandler): async def get(self, *args, **kwargs): self.write("hello world2") if __name__ == "__main__": app = web.Application([ ("/", MainHandler), ("/2/", MainHandler2) ], debug=True) app.listen(8888) tornado.ioloop.IOLoop.current().start()
同时访问以下两个url
http://127.0.0.1:8888/ http://127.0.0.1:8888/2/
返回结果如下
当我们先访问http://localhost:8888/ 然后立即 http://localhost:8888/2/,由于tornado的处理http请求是一种单线程的模式,http://localhost:8888/这个请求,如果在请求处理函数中使用同步IO(time.sleep(5)就属于同步
IO)
它会阻塞其他的请求处理,直到该请求执行完毕返回”hello world“,然后http://localhost:8888/2/的处理函数才会执行并返回hello world2。
所以通过上述实例,我们一定要注意,千万不要在tornado中使用同步IO。
tornado中url的映射配置
url 命名,直接使用tornado.web中的URLSpec进行实例化,传入name为url命名
tornado.web.URLSpec("/", MainHandler, name="index"),
通过 url 的名字进行重定向
self.redirect(self.reverse_url(url名字, "传递的参数"))
给handler 传入初始值
把字典中的name的值people传入给handler
people_db = { "name": "people" } urls = [ tornado.web.URLSpec("/people/(\d+)/?", PeopleIdHandler, people_db, name="people_id"), ]
通过 initialize 方法接受初始值
class PeopleIdHandler(web.RequestHandler): def initialize(self, name): self.db_name = name async def get(self, id, *args, **kwargs): print(self.db_name) self.redirect(self.reverse_url("people_name", "zhangbiao"))
完整实例
import time from tornado import web import tornado class MainHandler(web.RequestHandler): #当客户端发起不同的http方法的时候, 只需要重载handler中的对应的方法即可 async def get(self, *args, **kwargs): self.write("hello world") class PeopleIdHandler(web.RequestHandler): def initialize(self, name): self.db_name = name async def get(self, id, *args, **kwargs): print(self.db_name) self.redirect(self.reverse_url("people_name", "zhangbiao")) class PeopleNameHandler(web.RequestHandler): async def get(self, name, *args, **kwargs): self.write("用户姓名:{}".format(name)) class PeopleInfoHandler(web.RequestHandler): async def get(self, name, age, gender, *args, **kwargs): self.write("用户姓名:{},用户年龄:{},用户性别:{},".format(name, age, gender)) people_db = { "name":"people" } from tornado.web import url #配置如/people/1/ urls = [ tornado.web.URLSpec("/", MainHandler, name="index"), tornado.web.URLSpec("/people/(\d+)/?", PeopleIdHandler, people_db, name="people_id"), tornado.web.URLSpec("/people/(\w+)/?", PeopleNameHandler, name="people_name"), #配置如/people/zhangbiao/ tornado.web.URLSpec("/people/(?P<name>\w+)/(?P<age>\d+)/(?P<gender>\w+)/?", PeopleInfoHandler, name="people_info"), #配置如/people/name/age/gender/ ] if __name__ == "__main__": app = web.Application(urls, debug=True) app.listen(8888) tornado.ioloop.IOLoop.current().start()
define、options、parse_comand_line,parse_config_file
常量配置通过define()函数来定义,然后,通过options来获取
define: 定义一些可以在命令行中传递的参数以及类型
options:options是一个类,全局只有一个options
options.parse_comand_line:从命行行中读取参数
options.parse_config_file:从配置文件中读取参数
创建配置文件conf.cfg
port=8002
启动程序代码如下
from tornado import web import tornado from tornado.options import define, options, parse_command_line #define, 定义一些可以在命令行中传递的参数以及类型 define('port', default=8008, help="run on the given port", type=int) define('debug', default=True, help="set tornado debug mode", type=bool) # options.parse_command_line()从配置文件中读取 options.parse_config_file("conf.cfg") #options是一个类,全局只有一个options class MainHandler(web.RequestHandler): #当客户端发起不同的http方法的时候, 只需要重载handler中的对应的方法即可 async def get(self, *args, **kwargs): self.write("hello world") class MainHandler2(web.RequestHandler): #当客户端发起不同的http方法的时候, 只需要重载handler中的对应的方法即可 async def get(self, *args, **kwargs): self.write("hello world2") if __name__ == "__main__": app = web.Application([ ("/", MainHandler), ("/2/", MainHandler2) ], debug=options.debug) app.listen(options.port) tornado.ioloop.IOLoop.current().start()
在终端输入以下命令启动
python options_test.py --port=8002
返回结果如下
RequestHandler常用方法和子类
initialize 方法 用于初始化handler类的过程
from tornado.web import RequestHandler class MainHandler(RequestHandler): #入口 def initialize(self, db): #用于初始化handler类的过程 self.db = db def get(self, *args, **kwargs): pass people_db = { "name": "people" } import tornado urls = [ tornado.web.URLSpec("/", MainHandler,people_db, name="index"), ]
prepare 方法 用于真正调用请求处理之前的初始化方法
from tornado.web import RequestHandler class MainHandler(RequestHandler): #入口 def prepare(self): #prepare方法用于真正调用请求处理之前的初始化方法 #1. 打印日志, 打开文件 pass def get(self, *args, **kwargs): pass people_db = { "name": "people" } import tornado urls = [ tornado.web.URLSpec("/", MainHandler,people_db, name="index"), ]
on_finish 方法 请求结束执行的操作
from tornado.web import RequestHandler class MainHandler(RequestHandler): #入口 def initialize(self, db): #用于初始化handler类的过程 self.db = db def prepare(self): #prepare方法用于真正调用请求处理之前的初始化方法 #1. 打印日志, 打开文件 pass def on_finish(self): #关闭句柄, 清理内存 pass people_db = { "name": "people" } import tornado urls = [ tornado.web.URLSpec("/", MainHandler,people_db, name="index"), ]
write_error方法
重写自定义错误页面的实现。
如果error是由没有捕获的异常(包括HTTPError)引起的,通过kwargs[|”exc_info”]能获取exc_info元组。实现代码如下:
import traceback from tornado.web import RequestHandler class MainHandler(RequestHandler): #入口 def initialize(self, db): #用于初始化handler类的过程 self.db = db def prepare(self): #prepare方法用于真正调用请求处理之前的初始化方法 #1. 打印日志, 打开文件 pass def on_finish(self): #关闭句柄, 清理内存 pass #http方法 def get(self, *args, **kwargs): pass def write_error(self, status_code, **kwargs): self.write("oh,my god!出错啦!!!!请联系系统管理员。\n") self.write("呵呵,也没关系,我已经讲错误信息记录在日志中去了,系统管理员会看到的。\r\n") if "exc_info" in kwargs: self.write("错误信息为:\r\n") for line in traceback.format_exception(*kwargs["exc_info"]): self.write(line) self.finish() people_db = { "name": "people" } import tornado urls = [ tornado.web.URLSpec("/", MainHandler,people_db, name="index"), ] if __name__ == "__main__": from tornado import web app = web.Application(urls, debug=True) app.listen(8888) tornado.ioloop.IOLoop.current().start()
获取前端传递的参数
1、 get_argument、get_arguments方法 在get 和 post 方法中都可以使用
get_argument获得单个值,而get_arguments是针对参数存在多个值得情况下使用,返回多个值的列表。
request.arguments 获取所有的参数包括 url中的参数和表单中传递的
传入参数如下
后台打印结果如下
获取json数据
def post(self, *args, **kwargs): param = self.request.body.decode("utf8") data1 = json.loads(param) print(data1)
传入数据如下
后台打印结果如下
self.finish 方法 关闭长连接,还可以写回数据
def post(self, *args, **kwargs): self.finish({ "name":"bobby" }) self.wirte("haha") # 关闭了长连接,下面的不会执行
set_status(code) 方法 设置状态码
def post(self, *args, **kwargs): try: data1 = self.get_body_argument("name") except Exception as e: self.set_status(500) self.finish({ "name":"bobby" })
RequestHandler 子类用法:RedirectHandler、StaticFileHandler
RedirectHandler 和 self.redirect的区别
self.redirect 是临时重定向,主要用于业务逻辑的
RedirectHandler 是永久性的重定向
StaticFileHandler 静态文件处理器,指定静态资源的目录
简单用法
在static/test/test.jpg(放入了一张图片)
from tornado.web import StaticFileHandler, RedirectHandler #1. RedirectHandler #1. 301是永久重定向, 302是临时重定向,获取用户个人信息, http://www.baidu.com https #StaticFileHandler import time from tornado import web import tornado class MainHandler(web.RequestHandler): #当客户端发起不同的http方法的时候, 只需要重载handler中的对应的方法即可 async def get(self, *args, **kwargs): time.sleep(5) self.write("hello world") class MainHandler2(web.RequestHandler): #当客户端发起不同的http方法的时候, 只需要重载handler中的对应的方法即可 async def get(self, *args, **kwargs): self.write("hello world2") settings = { "static_path":"D:/BaiduYunDownload/971、Tornado从入门到进阶/资料/tornado-resources-master/tornado-resources/tornado_overview/chapter02/static", "static_url_prefix":"/static2/" } if __name__ == "__main__": app = web.Application([ ("/", MainHandler), ("/2/", RedirectHandler, {"url":"/"}), # 永久重定向 ("/static3/(.*)", StaticFileHandler, {"path": "D:/tornado_overview/chapter02/static"}) # 指定静态文件路径 ], debug=True, **settings) app.listen(8888) tornado.ioloop.IOLoop.current().start()
访问
http://127.0.0.1:8888/static3/test/test.jpg
返回结果
tornado的template
定义模板地址
首先我们需要定义一下模板所在地址,让tornado知道去哪里找模板,一般我们把地址写在入口文件中。下列代码static_path的值就是模板的地址
settings = { "static_path":"C:/projects/tornado_overview/chapter02/static", "static_url_prefix":"/static2/", "template_path": "templates" } if __name__ == "__main__": app = web.Application([ ("/", MainHandler2) ], debug=True, **settings) app.listen(8888) tornado.ioloop.IOLoop.current().start()
传递参数到模板中
当我们在handler处理好数据后,就可以把数据传递到相应的模板中去。
from tornado.web import StaticFileHandler, RedirectHandler from tornado import web import tornado from tornado.web import template class MainHandler2(web.RequestHandler): #当客户端发起不同的http方法的时候, 只需要重载handler中的对应的方法即可 async def get(self, *args, **kwargs): word = "你好" self.render("hello.html", word=word) settings = { "static_path":"C:/projects/tornado_overview/chapter02/static", "static_url_prefix":"/static2/", "template_path": "templates" } if __name__ == "__main__": app = web.Application([ ("/", MainHandler2) ], debug=True, **settings) app.listen(8888) tornado.ioloop.IOLoop.current().start()
templates/hello.html
<h1>{{ word }}</h1>
访问结果如下
填充及控制语句
在模板文件中填充变量,可以使用下列方式获取值:
{{ quantity }} {{ com(1) }}
同时tornado还支持一些简单的控制语句:
{%if 或者 for %} ... 这里是各种表达式 {%end%}
Tornado在所有模板中默认提供了一些便利的函数。它们包括:
- escape(s):替换字符串s中的&、<、>为他们对应的HTML字符。
- url_escape(s):使用urllib.quote_plus替换字符串s中的字符为URL编码形式。
- json_encode(val):将val编码成JSON格式。(在系统底层,这是一个对json库的dumps函数的调用。查阅相关的文档以获得更多关于该函数接收和返回参数的信息。)
- squeeze(s):过滤字符串s,把连续的多个空白字符替换成一个空格。
其他
{%set str='xxxx'%} 使用:{{str}
模板转义:转义是为了防止你的访客进行恶意攻击的,但当你不希望转移时,可以使用raw来阻止对变量转义。
{% raw mail %}
tornado的settings