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')