全栈工程师开发手册 (作者:栾鹏)
架构系列文章
这节中,我们来根据客户请求报文中的请求行(Request line) ,请求头部(Request Header),请求主体(Request body)的信息,来描述服务端如何处理。
请求行的格式:
再次重申:tornado 用 web.RequestHandler 类,来处理客户请求。每一次客户发来请求,先由服务器应用APP根据路由映射,调用处理类,生成一个RequestHandler类对象。这个类对象负责处理 客户请求的请求信息,并根据类的处理方法,生成返回的响应报文。
我们这里,根据客户的请求报文三个部分,来看看 客户的请求报文 提供的信息。
get方法,主要用来请求数据。client端将请求的信息以URL编码的信息明文传递给服务器。
post方法,主要用来传递数据给服务端。client端请求的信息以数据包形式传送给服务端。
post请求,常用来提交表单的私密数据。get请求,网页中文件加载等。
比如用户信息,用get表示请求用户信息,用post表示修改用户的信息等。
再如:Request-URL 为http://ip:/book/book_id
,即可利用get 方法通过 book_id 得到书籍的详细信息。也可利用post方法,提交书籍的信息,从而修改书籍有详细信息。
#以一个请求地址的两种 请求方法为例。
#在路由映射中添加:
# (r"/book/(\d+)", BookInfoHandler)
#添加处理类,注意这里的id来源。
class BookInfoHandler(tornado.web.RequestHandler):
def get(self,id):
self.write(id);
def post(self,id):
self.write(id + "post")
# 测试方法:在ubuntu中命令:
# 测试get:
#curl http://192.168.128.140:8000/book/12345612
# 测试post:
#curl -d aaa=bbb http://192.168.128.140:8000/book/12345612
URL书写风格
URL有两种表示风格。早期,查询字符串风格,在URL中以?表示传递的参数 ,比如在百度中搜索hello
,URL的请求地址:https://www.baidu.com/s?wd=hello&ie=UTF-8
。
后期,出现RESTFUL风格的表示方式(Representational State Transfe). 这种概念中,每一个URL 都表示一种资源,而将对资源的操作放在了请求体中。简单说来,也是一种HTTP的分层思想,将资源和操作分离开。
查询字符串风格 :
http://192.168.128.140:8000/book?id=123456
RESTFUL风格 :http://192.168.128.140:8000/book/id/123456
通过参数使用方法,通过路径调用方法
在客户端的请求报文中,网站开发者的自定义参数,出现在两个地方:URL的查询字符串中,和请求报文的请求体(Request body)中。
get_argument 获取URL查询字符串或请求体中的参数信息。而get_arguments 获取查询字符串或请求体中的信息对应元素列表。命令使用方法:get_argument(target_name,default,strip=true)
,注意返回的信息是unicode字符串。
get_query_argument ,get_query_arguments。只查询URL字符串信息。
get_body_argument, get_body_arguments 查询请求主体中信息。
# 常用get_argument 来获取参数。get_query_argument我在firefox测试才正常,而get_body_argument在curl下测试正常。所以建议使用get_argument.
# get_argument和get_arguments,都接受一个参数的多个值,但get_argument只返回这个参数的最后一个值,get_arguments 返回这个参数值的列表。参考tornado源码的说明 。
# 测试例子。添加路由:(r'/argument',ArgHandler) ,这里注意,如用上节的代码添加,需要先把根路由'/'删除或放在最下面,否则先匹配到根路由,就不再往下面匹配路由。
class ArgHandler(tornado.web.RequestHandler):
def get(self):
arg1 = self.get_argument('arg1', '')
print arg1
self.write(arg1)
# 这里再补充一个知识,便于服务器重启。
# 正常情况下,我们每次修改这个服务器程序,都要在服务器端重新运行启动命令:python server.py
# tornado为我们提供了一个服务器应用的参数,debug调试。开启会,如果代码有修改,它会重新加载,并且服务器出错,会在浏览器中返回错误信息。可在开发环境中使用,但不要在正式环境中使用。代码如此下:
app = tornado.web.Application(
handlers,
debug = True
)
#注意,handlers后,添加逗号。
第三节中,我们简单的说明了路由正则匹配的事情。这里我们需要注意几点:
链接路由请求,在路由匹配时,只要匹配到。就不再向后匹配。
如果使用正则分组,请求方法的参数要分组名保持个数相同。如果分组没命名,则按顺序传入参数。
服务中处理 请求时,分组 做为参数 传递。对参数进行操作。(倒序)
# 以第一例来说:(r"/book/(\d+)", BookInfoHandler),此处正则匹配时有一分组,分组将会以一个整体看待做为参数,传递给处理方法:BookInfoHandler。所以后面定义类的处理方法时有两个参数:get(self,id)。否则,web.RequestHandler类会报错。
# 分组命令写法:(r"/book/(?P\d+)",BookInfoHandler),此处将分组命名为:bid.那么后面处理方法:BookInfoHandler必须将bid 作为参数,而不能用名字做为参数。如:get(self,bid)
# 示例如下:
# match with,匹配路由:(r"/book/(?P\d+)",BookInfoHandler)
# 处理方法:倒着输出bid。
class BookInfoHandler(tornado.web.RequestHandler):
def get(self,bid):
self.write(bid[::-1])
# 运行服务器应用,在客户浏览器测试:http://192.168.128.140:8000/book/123456.
# 在浏览器会显示 :654321
每一次客户请求报文中请求头部包含的客户端信息,我们都封装在这次请求生成的RequestHandler对象的request属性中。比如说请求头部包含的客户地址表示:self.request.remote_ip
。
下面列出一些比较请求信息:
`* self.request.method HTTP请求方法。如:get,post,put等。
* self.request.url 请求的完整URL。 path:路径部分。query: 查询部分。
* self.request.body 请求主体。
* self.request.remote_ip , 客户端的IP地址作为字符串。如果HTTPServer.xheaders设置,将传递由负载均衡器
在X-Real-Ip或X-Forwarded-For头中提供的真实IP地址。
* self.request.cookie 客户存储的cookie值。
* self.request.files 文件属性中使用文件上传。
* self.request.connection 长连接。
* self.request.request_time 返回此请求执行所花费的时间。`
get_argument(name,default=_ARG_DEFAULT,strip=True)
get_arguments(name,strip=True)
get_body_argument(name,default=_ARG_DEFAULT,strip=True)
get_body_arguments(name,strip=True)
get_query_argument(name,default=_ARG_DEFAULT,strip=True)
get_query_arguments(name,strip=True):
一 RequestHandler.get_argument()和RequestHandler.get_arguments():
实际上是通过RequestHandler._get_argument()和RequestHandler._get_arguments()对HTTPServerRequest.arguments进行一些去空格,编码,默认值等操作。HTTPServerRequest.arguments包括GET和POST请求的所有参数。
get_argument()实际上是获取的get_arguments()[-1]。
class HTTPServerRequest(object):
attribute:arguments
GET/POST arguments are available in the arguments property, which
maps arguments names to lists of values (to support multiple values
for individual names). Names are of typestr
, while arguments
are byte strings. Note that this is different from
.RequestHandler.get_argument
, which returns argument values as
unicode strings.
二 RequestHandler.get_query_argument()和RequestHandler.get_query_arguments():
实际上是通过RequestHandler._get_argument()和RequestHandler._get_arguments()对HTTPServerRequest.query_arguments进行一些去空格,编码,默认值等操作。HTTPServerRequest.query_arguments只包括请求的url上所带的参数。
class HTTPServerRequest(object):
attribute: query_arguments
Same format asarguments
, but contains only arguments extracted
from the query string.
二 RequestHandler.get_body_argument()和RequestHandler.get_body_arguments():
HTTPServerRequest.body_arguments只包括请求body里所带的参数。
class HTTPServerRequest(object):
attribute: body_arguments
Same format asarguments
, but contains only arguments extracted
from the request body.
url = ‘127.0.0.1:8080/test?search_title=测试标题1&search_title=测试标题2&search_name=测试名称&test_body=测试’
若存在多个重复的参数名,利用get_argument(),get_query_argument只能获取最后一个,即’测试标题2’。利用get_arguments(),get_query_arguments()能获得一个unicode编码的list,得到所有相同参数名的参数。
get_body_argument(),get_body_arguments()不能获取url中的参数。
search_title =self.get_argument(‘search_title’,’’) # 测试标题2
search_titles = self.get_arguments(‘search_title’, []) # unicode编码的list, 实际为[测试标题1,测试标题2]
search_name =self.get_query_argument(‘search_name’,’’) # 测试名称
search_names =self.get_query_arguments(‘search_name’,[]) # unicode编码的list, 实际为[测试名称]
test_body_argument = self.get_body_argument(‘test_body’,’’) # ‘’
表单中多个同名,利用get_arguments()和get_body_arguments()获取list。
flow_title =self.get_argument(‘flow_title’,’’) # 测试流程
test_flow_title=self.get_body_argument(‘flow_title’,’’) # 测试流程
step_titles =self.get_arguments(‘step_title’,[]) # list
tesst_step_titles=self.get_body_arguments(‘step_title’,[]) # list
var data = {‘test_arg’, ‘v1’} // 同个’test_arg’获取
var list_data = {‘test_arg’, [‘v1’, ‘v2’]} // 通过’test_list[]'获取
test_arg=self.get_argument(‘test_arg’, ‘’)
test_args=self.get_arguments(‘test_arg[]’, ‘’) # 获取到list
tornado/web.py
tornado/httputil.py
tornado/escape.py