2.tornado请求与响应
之前我们介绍了tornado的基础流程,但还遗留了一些问题。今天我们就来解决遗留问题并学习新的内容
settings,使用tornado.web.Application(handler, **settings),我们却不知道这个settings到底是什么,究竟有什么作用,今天就来介绍一下
settings是一个字典,主要保存一些配置选项
debug:设置tornado是否在调试模式下,默认为False,即在生产模式下
自动重启,tornado应用会监控源代码文件,当有保存改动时便会自动重启服务器,类似于Django,可以减少手动重启的次数,提高开发效率
如果保存代码后有错误会导致重启失败,修改错误需要手动重启
可以通过autoreload = True设置
取消缓存编译的模板:有时候我们明明修改了模板文件,但是页面显示的内容并没有变化,就是因为用的还是缓存文件。因此debug = True,可以保证每次加载的模板文件都是最新的,也可以通过compiled_template_cache=True单独设置
取消缓存静态文件的hash值:这和取消编译模板是类似的,有时候我们修改css,但是页面颜色或者字体并没有发生变化。 有了debug=True,可以保证每次加载的静态文件都是最新的,也可以通过compiled_static_cache=True单独设置
提供追踪信息:可以通过server_traceback=True单独设置
static_path:设置静态文件目录,
注意:寻找静态文件是tornado去找,类似Django,你要告诉tornado要去什么地方去找静态文件
template_path:设置模板文件目录,和设置静态文件目录类似
注意:寻找静态文件是tornado去找,类似Django,你要告诉tornado要去什么地方去找静态文件
配置文件如下:
import os options = {"port": 7777} BASE_DIR = os.path.dirname(os.path.abspath(__file__)) settings = {"static_path": os.path.join(BASE_DIR, "static"), "template_path": os.path.join(BASE_DIR, "templates"), "compiled_static_cache": True, "compiled_template_cache": True, "server_traceback": True}
关于静态文件路径和模板文件路径,再次强调,是tornado去找。而且后面还会用到模板语言,必须要让tornado知道相应的路径在哪里。
在settings中指定static_path和template_path,那么tornado便会到相应的路径下去寻找。
--------------------------------------------------------------------------------------------------------------------------------------------------------------
下面介绍一下tornado的路由,路由在上一章我们知道,是用来指定url和handler之间的映射关系。
当一个url过来会进行解析,然后执行对应的handler,tornado的路由还可以加一些别的参数
view.py
import tornado.web class SatoriHandler(tornado.web.RequestHandler): # 该方法会在执行http方法之前执行 # 可以传入参数,这个参数则是我们在定义路由的时候指定 def initialize(self, girl1, girl2): self.girl1 = girl1 self.girl2 = girl2 def get(self, *args, **kwargs): print(self.girl1, self.girl2) self.write("my name is satori") class MashiroHandler(tornado.web.RequestHandler): def get(self, *args, **kwargs): # url的反向解析 # 我们在定义路由的时候可以指定一个name="xxx",那么self.reverse_url(name),便会获取相应的url # 这便是url的反向解析,通过指定的name获取定义的url url = self.reverse_url("satori") self.write(f'<h1><a href="{url}">还是喜欢satori</a></h1>')
application.py
import tornado.web from views import view import config # 我们可以自定义一个Application,但是要继承tornado.web下的Application class Application(tornado.web.Application): def __init__(self): handlers = [ # 我们在SatoriHandler的initialize中定义了girl1和girl2 # 那么girl1和girl2就在包含相应handler的路由中定义 # 以字典的形式传值即可 (r"/satori", view.SatoriHandler, {"girl1": "satori", "girl2": "koishi"}), # 由于我们指定了name,那么name也要在包含相应handler的路由中定义 # 但是name比较特殊,我们不能用一般的路由定义,要使用tornado.web.url() # 然后使用self.reverse_url(name)获取到的便是name所在路由的url # 那么在页面渲染出来的便是一个a标签 tornado.web.url(r"/koishi", view.SatoriHandler, {"girl1": "satori", "girl2": "koishi"}, name=r"satori"), (r"/mashiro", view.MashiroHandler) ] super(Application, self).__init__(handlers=handlers, **config.settings)
# 输入localhost:7777/satori,得到页面如下
同时在pycharm中也打印了girl1和girl2的值
# 输入localhost:7777/koishi,得到页面如下
pycharm中输出如下
两个url的handler是一样的
最后再来输入localhost:7777/mashiro
也显示了相应的字符串,而且是一个被渲染为一个a标签,我们点击一下
又跳转回去了,因为name="satori"所在路由的url是r"/koishi",那么通过在MashiroHandler中进行反向解析获取到的url就是r"/koishi"
那么点击之后会再次跳转到localhost:7777/koishi,从而执行SatoriHandler
从而在pycharm中再次输出,总共三次
--------------------------------------------------------------------------------------------------------------------------------------------------------------
利用HTTP协议向服务器传递参数
1.提取url当中的特定部分
application.py
import tornado.web from views import view import config # 我们可以自定义一个Application,但是要继承tornado.web下的Application class Application(tornado.web.Application): def __init__(self): handlers = [ # 可以支持正则表达式,我们定义了三个分组 # 那么用户输入的内容会根据分组依次对应传到handler的get函数里面 (r"/satori/(\w+)/(\w+)/(\w+)", view.SatoriHandler), # 由于我们指定了分组的名字,就类似于关键字参数 # adj1、adj2、adj3会分别对应get函数里面的adj1、adj2、adj3 (r"/mashiro/(?P<adj1>\w+)/(?P<adj2>\w+)/(?P<adj3>\w+)", view.MashiroHandler) ] super(Application, self).__init__(handlers=handlers, **config.settings)
view.py
import tornado.web class SatoriHandler(tornado.web.RequestHandler): def get(self, adj1, adj2, adj3): # 这里的参数会和分组的顺序保持一致 self.write(f"<h1>satori {adj1}, {adj2}, {adj3}</h1>") class MashiroHandler(tornado.web.RequestHandler): def get(self, adj2, adj3, adj1): # 由于在路由中指定了名称,那么函数中参数的顺序便无所谓了 self.write(f"<h1>mashiro {adj1}, {adj2}, {adj3}</h1>")
在浏览器中输入满足路由映射的url,会发现按照顺序依次打印了出来
再来试试另一个路由
可以看到即便我们在MashiroHandler中定义的参数没有什么顺序,但是在页面中依旧按照我们输入url的顺序打印了出来,
这是因为我们定义路由的时候指定了名字,那么在执行get函数的时候,相当于adj1="elegant", adj2="beautiful", adj3="cute",
2.get方式传递参数
当我们输入localhost:7777/satori?a=1&b=2&c=3的时候,可以在程序当中获取相应的a,b,c的值。
可以回想一下requests,requests.get函数中有一个params参数,传入{"a": 1, "b": 2, "c": 3},便会自动和请求的url进行组合,从而得到类似于上面的新的url
那么反过来获取相应的a,b,c的值也是可以的
application.py
import tornado.web from views import view import config # 我们可以自定义一个Application,但是要继承tornado.web下的Application class Application(tornado.web.Application): def __init__(self): handlers = [ (r"/satori", view.SatoriHandler), ] super(Application, self).__init__(handlers=handlers, **config.settings)
view.py
import tornado.web class SatoriHandler(tornado.web.RequestHandler): def get(self): ''' 函数的原型是self.get_argument(name, strip=True) 其实这个函数是self.get_query_argument(用于获取get请求的参数)和self.get_body_argument(用于获取post请求的参数)两个的函数的组合 而self.get_argument既可以用于get也可以用于post,因此我们一般都使用self.get_argument 参数: name:返回get请求参数字符串中指定参数的值 如果出现多个同名参数,返回最后一个 strip:是否过滤掉两边的空格,默认为True表示过滤 ''' a = self.get_argument("a") b = self.get_argument("b") c = self.get_argument("c") d = self.get_arguments("d") self.write(f"参数a={a}, 参数b={b}, 参数c={c}, 参数d={d}")
首先a,b无需解释,至于c,由于我们是self.get_argument,所以只会获取最后一个,但是对于d,使用self.get_arguments,会获取一个列表,如果只有一个值,获取的仍是个列表,列表只有一个元素
3.post方式传递参数
首先templates目录里面创建一个html文件 ,satori.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>古明地觉</title> </head> <body> <form action="/satori" method="post"> 姓名:<input type="text" name="name"> <p></p> 性别:<input type="text" name="gender"> <p></p> 登场:<input type="text" name="from"> <p></p> 昵称: <input type="checkbox" name="nickname" value="少女觉">少女觉 <input type="checkbox" name="nickname" value="觉大人">觉大人 <input type="checkbox" name="nickname" value="小五">小五 <input type="submit" value="提交"> </form> </body> </html>
application.py
import tornado.web from views import view import config # 我们可以自定义一个Application,但是要继承tornado.web下的Application class Application(tornado.web.Application): def __init__(self): handlers = [ (r"/satori", view.SatoriHandler), ] super(Application, self).__init__(handlers=handlers, **config.settings)
view.py
import tornado.web class SatoriHandler(tornado.web.RequestHandler): def get(self): # self.write表示写入字符串 # self.render表示将一个模板读取进来,进行渲染(替换掉模板语言),返回给用户 # 而且我们只是指定了文件名,tornado居然能找得到,因为我们在settings中定义了template_path # 因此在执行self.render的时候tornado会自动到相应目录下去寻找模板文件 self.render("satori.html") def post(self, *args, **kwargs): # 我们在表单中定义了name="xxx" # 因此self.get_argument的参数就是name gender = self.get_argument("gender") name = self.get_argument("name") _from = self.get_argument("from") nickname = self.get_arguments("nickname") self.write(f"你的名字是{name}, 性别是{gender}, 来自于{_from}, 昵称是{' '.join(nickname)}")
浏览器中输入如下:
点击提交之后
下面介绍一下HTTPServerRequest对象
想一下我们使用requests模块访问得到的response一样,这个response是服务端给我们返回的信息,如果加上了request表示客户端向服务端请求时的信息
因此request:请求,response:响应
所以tornado的request作用就是储存了请求的相关信息
method:http请求的方式
host:请求的主机名
uri:请求的完整资源地址,包括路径和get参数查询部分,注意是uri不是url
path:请求的路径部分
query:请求参数部分
version:使用的http版本
headers:请求的协议头是一个字典类型
body:请求体数据
remote_ip:客户端ip
view.py,其他的文件不变
import tornado.web class SatoriHandler(tornado.web.RequestHandler): def get(self): self.write("<h1>请求的信息如下:</h1>") self.write(f"<h2>self.request.method={self.request.method}</h2>") self.write(f"<h2>self.request.host={self.request.host}</h2>") self.write(f"<h2>self.request.uri={self.request.url}</h2>") self.write(f"<h2>self.request.path={self.request.path}</h2>") self.write(f"<h2>self.request.version={self.request.version}</h2>") self.write(f"<h2>self.request.headers={self.request.headers}</h2>") self.write(f"<h2>self.request.body={self.request.body}</h2>") self.write(f"<h2>self.request.remote_ip={self.request.remote_ip}</h2>")
输入localhost:7777/satori,得到页面如下
tornado.httputil.HTTPFile对象
接收到的是一个文件对象,属性有name,body,content_type
filename: 文件名
body: 文件的数据实体
content_type: 文件类型
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>古明地觉</title> </head> <body> <!--如果想上传文件,那么必须要加上enctype="multipart/form-data"--> <form action="/satori" method="post" enctype="multipart/form-data"> <input type="file" name="picture"> <input type="file" name="picture"> <input type="file" name="txt"> <input type="submit" value="提交"> </form> </body> </html>
view.py
import tornado.web import os class SatoriHandler(tornado.web.RequestHandler): def get(self): self.render("satori.html") def post(self, *args, **kwargs): BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) file_dict = self.request.files ''' 关于这里的file_dict,要详细的说一下 首先我们在html文件中,写了如下代码 <input type="file" name="picture"> <input type="file" name="picture"> <input type="file" name="txt"> 那么在文件上传提交之后,我们获取file_dict便是一个字典 file_dict = { "picture": [{"filename": "xxx", "body": "xxx", "content_type": "xxx"}, {"filename": "xxx", "body": "xxx", "content_type": "xxx"}], "txt": [{"filename": "xxx", "body": "xxx", "content_type": "xxx"}] } 我们在html中定义了name="picture"和name="txt"两个上传文件的input,那么"picture"和"txt"就是这个file_dict的两个key 每个key,分别对应一个列表。每个列表中存储的字典,便包含了上传文件的信息。因为可能上传多个文件,所以有多个字典。 比如我们这里的picture,列表里面就有两个字典,因为我们以name="picture"上传两个文件。 如果只有一个文件,那么key对应的value仍是一个列表,只不过这个列表里面只有一个字典,比如我们这里的txt example:我们上传了,satori.jpg(name="picture")、koishi.jpg(name="picture")、mmp.txt(name="txt") 那么file_dict如下: file_dict = { "picture": [{"filename": "satori.jpg", "body": b"二进制文件内容", "content_type": "img/jpeg"}, {"filename": "koishi.jpg", "body": b"二进制文件内容", "content_type": "img/jpeg"}], "txt": [{"filename": "mmp.txt", "body": b"二进制文本内容", "content_type": "text/plain"}] } ''' # 接下来便以上面的example进行上传 ''' name依次循环--->"picture","txt" ''' for name in file_dict: ''' file_arr依次循环--->[{"filename": "satori.jpg", "body": b"二进制文件内容", "content_type": "img/jpeg"}, {"filename": "koishi.jpg", "body": b"二进制文件内容", "content_type": "img/jpeg"}], [{"filename": "mmp.txt", "body": b"二进制文本内容", "content_type": "text/plain"}] ''' file_arr = file_dict[name] ''' file_obj依次循环--->"{filename": "satori.jpg", "body": b"二进制文件内容", "content_type": "img/jpeg"}, {"filename": "koishi.jpg", "body": b"二进制文件内容", "content_type": "img/jpeg"}, {"filename": "mmp.txt", "body": b"二进制文本内容", "content_type": "text/plain"} ''' for file_obj in file_arr: # filename:文件名 filename = file_obj["filename"] # body:文件内容 body = file_obj["body"] # 文件类型 content_type = file_obj["content_type"] # 创建了一个upload_files目录,专门用来存放上传的文件 with open(os.path.join(BASE_DIR, "upload_files", filename), "wb") as f: f.write(body) self.write(f"{filename}上传成功,文件类型为{content_type}<br>")
点击提交
再来看看upload_files目录,有没有上传的文件
显然上传是成功了的,以上操作便可以完成文件的上传
--------------------------------------------------------------------------------------------------------------------------------------------------------------
响应输出
1. self.write
原型:self.write(chunk)
作用:将数据写到缓冲区
import tornado.web import json class SatoriHandler(tornado.web.RequestHandler): def get(self): self.write("我是self.write") self.write("我的作用是将数据写到缓冲区") # self.write还可以写入json文件 dic = {"name": "古明地觉", "from": "东方地灵殿"} # 可以手动转化为json文件,那么Content-Type属性值为text/html self.write(json.dumps(dic)) # 也可以直接利用write方法写入字典,那么Content-Type属性值为application/json self.write(dic) # self.finish(),表示手动关闭当次请求通道 # 在self.finish()下面就不要在self.write了,否则报错 self.finish()
2. self.set_header(name, value)
作用:手动设置一个名为name,值为value的响应头字段
参数:name,字段名称,value,字段值
import tornado.web class SatoriHandler(tornado.web.RequestHandler): def get(self): self.set_header("satori", "koishi") self.write("我设置了一个响应头字段,可以检查元素看看")
3. set_default_header()
作用:在执行http方法之前被调用,可重写该方法预先设置默认的headers
import tornado.web class SatoriHandler(tornado.web.RequestHandler): def set_default_headers(self): self.set_header("zgg", 666) self.set_header("mmp", 250) def get(self): self.set_header("satori", "koishi") self.set_header("love", "satori") self.write("我在两个地方设置了响应头字段")
4. self.set_status(status_code, reason=None)
作用:为相应设置状态码
参数:status_code,状态码值,为int,如果reason=None,那么状态码必须合法。
reason,描述状态码的词组,为string
import tornado.web class SatoriHandler(tornado.web.RequestHandler): def get(self): self.set_header("satori", "koishi") self.set_header("love", "satori") # 404是合法的状态码,所以可以不指定reason self.set_status(status_code=404) self.write("我设置了状态码,去看看")
import tornado.web class SatoriHandler(tornado.web.RequestHandler): def get(self): self.set_header("satori", "koishi") self.set_header("love", "satori") # 404是合法的状态码,指定一下reason self.set_status(status_code=404, reason="success") self.write("我设置了404,但是reason是success,去看看")
import tornado.web class SatoriHandler(tornado.web.RequestHandler): def get(self): self.set_header("satori", "koishi") self.set_header("love", "satori") # 指定一个不存在的状态码,这时候要指定reason,否者状态码后面的描述是Unknow # 因为我们在指定404的时候,这些合法的状态码都是有意义的 # 如404代表页面未找到,200代表成功,所以即使我们不指定reason,也会自动帮你添加 # 但是如果指定一个不存在的状态码,那么最好指定reason self.set_status(status_code=666, reason="you 666") self.write("我设置了一个不合法的状态码,去看看")
5. self.redirect(url)
作用:重定向到某个url
import tornado.web class SatoriHandler(tornado.web.RequestHandler): def get(self): self.redirect("http://www.baidu.com")
输入网址回车,会自动跳转到百度
6. self.send_error(status_code=500, **kwargs)
作用:抛出http错误状态码,默认为500,抛出错误后,tornado会自动调用write_error进行处理,并返回给页面
注意:在self.send_error之后就不要再响应输出了
7. self.write_error(status_code=500, **kwargs)
作用:用来处理self.send_error抛出的异常,并返回给浏览器错误界面
import tornado.web class SatoriHandler(tornado.web.RequestHandler): def write_error(self, status_code, **kwargs): # self.send_error传入的状态码,会自动传入write_error当中 # self.write_error和self.send_error中的status_code是相同的 if status_code == 404: self.set_status(404, "damn it!!!") self.write("你妹的,活该资源找不到") def get(self): DoesSatoriLoveMe = self.get_argument("DoesSatoriLoveMe") if DoesSatoriLoveMe in ["yes", "YES", "Yes"]: self.write("哼。。给你个正常页面吧") else: self.send_error(404)
tornado请求与响应就先到这,下一节将介绍tornado的接口调用顺序与模板
2.tornado请求与响应的更多相关文章
- tornado请求与响应
tornado中处理请求与响应的类如下, 所有视图类必须继承该类: tornado.web.RequestHandler 一. 响应之self.write()方法 1. 该方法可返回值的类型: 当返 ...
- tornado的请求与响应
tornado请求与响应相关 一.配置文件config.py 中的settings 有哪些配置: debug:设置tornado是否工作再调试模式下,默认为false 即工作再生产模式下 true的特 ...
- 2.(基础)tornado的请求与响应
之前我们介绍了tornado 的基础流程,但是还遗留了一些问题,今天我们就来解决这些遗留问题并学习新的内容 settings,使用tornado.web.Application(handler, ** ...
- tornado 03 请求与响应
tornado 03 请求与响应 一.请求与响应 浏览器与服务器之间沟通的到底是什么信息 #服务器在后台一直保持运行着 #浏览器通过URL(路由.地址)发送请求 #服务器接收请求了通过tornado处 ...
- Django底层剖析之一次请求到响应的整个流程
As we all know,所有的Web应用,其本质上其实就是一个socket服务端,而用户的浏览器就是一个socket客户端. #!/usr/bin/env python #coding:utf- ...
- 初入网络系列笔记(4)HTTP请求和响应
一.借鉴说明,本博文借鉴以下博文 1.starok,HTTP必知必会,http://www.cnblogs.com/starstone/p/4890409.html 2.CareySon,HTTP协议 ...
- http协议(二)请求和响应报文的构成
http协议用于客户端和服务器之间的通信,请求访问资源的一方称为客户端,而提供资源响应的一方称为服务器端. 下面就是客户端和服务端之间简单的通信过程 PS:请求必须从客户端建立通信,服务端没收到请求之 ...
- iOS开发——网络篇——HTTP/NSURLConnection(请求、响应)、http响应状态码大全
一.网络基础 1.基本概念> 为什么要学习网络编程在移动互联网时代,移动应用的特征有几乎所有应用都需要用到网络,比如QQ.微博.网易新闻.优酷.百度地图只有通过网络跟外界进行数据交互.数据更新, ...
- struts2基础——请求与响应、获取web资源
一.请求与响应 Action1.含义:(1) struts.xml 中的 action 元素,也指 from 表单的 action 属性,总之代表一个 struts2 请求.(2) 用于处理 Stru ...
随机推荐
- Linux与BSD不同
https://linux.cn/article-3186-1.html https://www.howtogeek.com/190773/htg-explains-whats-the-differe ...
- Android 面试收集录5 消息机制
1.消息机制概述 1.1.消息机制的简介 在Android中使用消息机制,我们首先想到的就是Handler. 没错,Handler是Android消息机制的上层接口. Handler的使用过程很简单, ...
- 1,版本控制git--仓库管理
再开始这个话题之前,让我想起了一件很痛苦的事情,在我大学写毕业论文的时候,我当时的文件是这样保存的 毕业论文_初稿.doc 毕业论文_修改1.doc 毕业论文_修改2.doc 毕业论文_修改3.d ...
- 《Cracking the Coding Interview》——第4章:树和图——题目5
2014-03-19 04:11 题目:设计算法检查一棵二叉树是否为二叉搜索树. 解法:既然是二叉搜索树,也就是说左子树所有节点都小于根,右子树所有节点都大于根.如果你真的全都检查的话,那就做了很多重 ...
- centos6安装openfire(mysql)
一.安装JDK 我的系统是centos6.8x64,首先安装jdk1.7.0 二.安装openfire 我装的包是:openfire-3.9.3-1.i386.rpm,复制到/opt目录 #rpm - ...
- Day3 UI:7种常用控件、4种基本布局
Android常用控件 TextView <TextView android:id="@+id/text_view" android:layout_width="m ...
- 【Android】实验7 BindService模拟通信 截止提交日期2016.5.3
实验7 BindService模拟通信 [目的] 实现启动端和BindService之间的双向通信 [要求] 1) 实现从启动端传递一个数据至BindService端: 2) 实现使用Bind ...
- 【python】用python爬取中科院院士简介信息
018/07/09 23:43 项目名称:爬取中科院871个院士的简介信息 1.爬取目的:中科院871个院士的简介信息 2.爬取最终结果: 3.具体代码如下: import re # 不用安装(注意! ...
- luajit的字节码
http://blog.csdn.net/zzz3265/article/details/41146569 这里写出了luajit的字节码
- 【bzoj1875】[SDOI2009]HH去散步 矩阵乘法
题目描述 一张N个点M条边的无向图,从A走到B,要求:每一次不能立刻沿着上一次的边的反方向返回.求方案数. 输入 第一行:五个整数N,M,t,A,B. N表示学校里的路口的个数 M表示学校里的路的条数 ...