environ 环境参数html
考虑到后期的事宜,引入第三方库 webobweb
https://docs.pylonsproject.org/projects/webob/en/stable/index.htmlapi
1、webob.request对象数据结构
将环境参数解析并封装成request对象app
使用方法:ide
GET 函数 |
发送的数据是URL的查询字符串,在request头部信息中工具 就是一个字典MultiDict,封装着查询字符串post |
POSTurl |
提交,数据放在请求的body中,可是也能够同时使用QERY_STRING |
只须要作一件事情:将其封装,都经过对象访问便可
这样的方式比字典访问好太多了
若是咱们单用environ的方式进行定义的话会很麻烦,必须写明下面信息
def app(environ:dict,start_response):
html = '<h1>hi</h1>'
start_response("200 OK", [('Content-Type','text/html; charset=utf-8')])
return [html.encode()]
若是使用webob的方式,直接return它的方法就能够了
涉及模块功能:
· Request
· Response
须要用的功能
· webob.dec -- WSGIfy decorator
· webob.exc -- WebOb Exceptions
· webob.static -- Serving static files
安装webob
pip install webob
分别打印如下信息查看结果:
from wsgiref.simple_server import make_server, demo_app
from urllib.parse import parse_qs,parse_qsl #获取传递的查询信息
from webob import Request,Response
def app(environ:dict, start_response):
qstr = environ.get('QUERY_STRING')
print('~~~',qstr)
request = Request(environ)
print(request.method) # 请求的方法
print(request.path) # 请求路径
print(request.GET) # 请求方法GET是否有数据
print(request.POST) # 请求方法POST是否有数据
print(request.params) # MultiDict
html = '<ha>hello request</h1>'
start_response("200 OK", [('Content-Type','text/plain; charset=utf-8')])
return [html.encode()]
if __name__ == "__main__":
ip = '127.0.0.1'
port = 9999
server = make_server(ip,port,app)
server.serve_forever()
server.server_close()
返回信息以下:
qstr:
method: GET
path: /
GET: GET([])
POST: <NoVars: Not a form request>
params: NestedMultiDict([])
127.0.0.1 - - [26/Dec/2017 19:43:59] "GET / HTTP/1.1" 200 22
访问字符串查询
http://127.0.0.1:9999/index.html?id=777&name=wang&name=chao
返回以下:
127.0.0.1 - - [26/Dec/2017 19:46:16] "GET /index.html?id=777&name=wang&name=chao HTTP/1.1" 200 22
qstr: id=777&name=wang&name=chao
method: GET
path: /index.html
GET: GET([('id', '777'), ('name', 'wang'), ('name', 'chao')])
POST: <NoVars: Not a form request>
params: NestedMultiDict([('id', '777'), ('name', 'wang'), ('name', 'chao')])
MuliDict
容许一个key存多个值,下面红色部分就是结果
再访问一个
qstr: name=tom&arg=19&age=18
method: GET
path: /index.html
GET: GET([('name', 'tom'), ('arg', '19'), ('age', '18')])
POST: <NoVars: Not a form request>
params: NestedMultiDict([('name', 'tom'), ('arg', '19'), ('age', '18')])
127.0.0.1 - - [26/Dec/2017 19:53:34] "GET /index.html?name=tom&arg=19&age=18 HTTP/1.1" 200 22
为什么这么设计:
在网页开发中,在表单中都是有name的属性,name有可能重名,一旦重名返回的就是name=xxx&name=xxx&name=xxxnu
这样就须要用某种数据结构去接应它
导入模块MultiDict
from webob.multidict import MultiDict
建立一个实例:
from webob.multidict import MultiDict
md = MultiDict()
md[1] = 2
md.add(1,'b')
md.add(1,'com')
print(md.get(1))
print(md.getall(1))
返回以下:
com # 若是是get方法则返回最新加入的结果
[2, 'b', 'com'] # 若是getall则以列表方式返回全部add过的value
POST
POST没有值,借助postman工具传递
提交一样的value,
返回以下:
127.0.0.1 - - [26/Dec/2017 20:10:47] "POST / HTTP/1.1" 200 22
qstr:
method: POST
path: /
GET: GET([])
POST: MultiDict([('name', 'wang'), ('name', 'chao')])
params: NestedMultiDict([('name', 'wang'), ('name', 'chao')])
若是不关心什么方法提交,只关心数据直接打印params便可
web.Response对象
from webob import Response
res = Response()
print('status: ',res.status)
print('content_type: ',res.content_type)
print('charset: ',res.charset)
print('status_code: ',res.status_code)
from webob import Response
res = Response()
print('status: ',res.status)
print('content_type: ',res.content_type)
print('charset: ',res.charset)
print('status_code: ',res.status_code)
返回以下:
status: 200 OK
content_type: text/html
charset: UTF-8
status_code: 200
打印response头部信息
print(res.headerlist)
[('Content-Type', 'text/html; charset=UTF-8'), ('Content-Length', '0')]
经过这样的能够构造状态和header,主要的目的是将start_response替换掉
查看Response()源码
res = Response() 《--查看源码
依次点开下面的方法查看过程
print('status: ',res.status)
print('content_type: ',res.content_type)
print('charset: ',res.charset)
print('status_code: ',res.status_code)
res.status_code = 404 ##
status_code 代码解读:
def _status_code__set(self, code):
try:
self._status = '%d %s' % (code, status_reasons[code])
except KeyError:
self._status = '%d %s' % (code, status_generic_reasons[code // 100])
#找到如下信息,发现已经帮咱们定义好了,不须要关心
status_code = status_int = property(_status_code__get, _status_code__set,
doc=_status_code__get.__doc__)
这里面的http虽然server作了不少事情,可是对咱们来说仍是不方便因此须要借助第三方库
查看Response源码以下:
若是是一个App实例能否用__call__方法来实现
查看源码:发现是一个__call__ 方法,说明能够直接调用
def __call__(self, environ, start_response):
"""
WSGI application interface
"""
if self.conditional_response:
return self.conditional_response_app(environ, start_response)
headerlist = self._abs_headerlist(environ) #response须要用到,由于请求头获取后要作响应头
start_response(self.status, headerlist)
if environ['REQUEST_METHOD'] == 'HEAD':
# Special case here...
return EmptyResponse(self._app_iter)
return self._app_iter
_safe_methods = ('GET', 'HEAD')
代码解读:
def __call__(self, environ, start_response):
传递了两个参数,
environ : 包含了全部的请求信息的字典,
start_response : 返回的头部信息
将头部信息经过_abs_headerlist 方法进行封装
headerlist = self._abs_headerlist(environ)
若是head头部信息,则返回一个空相应,若是不是则返回一个可迭代_app_iter
不过是将所谓的正文写在一个可迭代对象里面进行封装
start_response(self.status, headerlist)
if environ['REQUEST_METHOD'] == 'HEAD': #若是传进来的是一个HEAD是一个‘REQUEST_METHOD’ 则返回一个空,不然封装传出一个可迭代列表
# Special case here...
return EmptyResponse(self._app_iter)
return self._app_iter
由此能够获得下面的代码:
from webob import Response,Request
def app(environ,start_resonse): #environ 获取全部信息
request = Request(environ)
print(request.method)
print(request.path)
print(request.query_string)
print(request.GET)
print(request.POST)
print(request.params)
#构建Response
#响应处理,若是调用这个实例有一个默认实现
res = Response()
res.status_code = 200
print(res.content_type)
html = 'hhh'.encode()
res.body = html #将响应的body写在这里就能够了
return res(environ, start_response) #经过__call__ 方法实现return
if __name__ == "__main__":
ip = '127.0.0.1'
port = 9999
server = make_server(ip,port,app)
server.serve_forever()
server.server_close()
既然__call__有意义,那么是否能够直接调用?
当业务逻辑完成以后则进行response
res = response() 默认是可行的
若是什么都不想改则进行res.body = xxx 直接返回
若是改为这样的语句:
return res(environ, start_response)
传递environ, start_response 这两个参数与
start_response(res,status,res.headerlist)
return [html.encode()]
有什么关联?
return res(environ, start_response)
实际是在调用response __call__方法,实际是一个实例的
这个__call__方法实际完成中实际是存有值的,就是body
由于在调用call方法 值已经赋给了res.body属性,状态码若是没有被赋值,则自动调用下面代码
start_response(self.status, headerlist)
将状态吗和headerlist传递进来
headerlist就是传过去的类型html ,实际都是默认的属性
__call__方法不过是实现了一个相似自定义的接口
传递这个接口以前是执行了一个实例的方法,这个实例是经过res = Response()建立出来的
调__call__方法就至关于将environ, start_response 传递,以后执行返回响应头和可迭代对象
只不过这个可迭代对象封装在body里, 而这个body由被封装在app.iter中
最后改进后的代码以下:
from webob import Request,Response
from wsgiref.simple_server import make_server, demo_app
def application(environ:dict,start_response):
request = Request(environ)
print('method: ',request.method)
print('path: ',request.path)
print('GET: ',request.GET)
print('POST: ',request.POST)
print('parms: ', request.params)
print('query_str: ',request.query_string)
#构建response
res = Response('<h1>hahah</h1>')
return res(environ,start_response)
ip = '127.0.0.1'
port = 9999
server = make_server(ip,port,application)
server.serve_forever()
server.server_close()
webob.dec 装饰器
https://docs.pylonsproject.org/projects/webob/en/latest/api/dec.html?highlight=wsgify
wsgify装饰器将一个普通函数转变成WSGI应用程序
导入模块
from webob.dec import wsgify
官方说明依然须要实例化一个response
例:
@wsgify
def myfunc(req:Request):
return Response('hey here')
用装饰器,并传递参数,这样就是被包裹过的request,
当返回这个值,则就被看成它的response
这个response必须用webob的response方法
@wsgify
def myfunc(request:Request):
return Response('hey here')
if __name__ == "__main__":
ip = '127.0.0.1'
port = 9999
try:
server = make_server(ip,port,myfunc)
except:
server.serve_forever()
server.server_close()
装饰的函数应该具备一个参数,这个参数就是被包装后的request类型,是对字典environ的对象化后的实例,可是返回值必须是一个webob.Resopnse类型,因此须要在函数中return webob.Resopnse类型
return Response('hey here')