[Python WEB開發] 使用WSGI開發類Flask框架 (二)
WSGI Web服務器網關接口
WSGI主要規定了Web服務器如何與Web應用程序進行通信,以及如何將Web應用程序鏈接在一起來處理一個請求。
wsgiref Python中的WSGI參考模塊
一、WSGI 應用程序端:
1、 根據WSGI定義,應用程序應該是可調用對象
2、該可調用對象必須有兩個固定參數:一個是含有服務器環境變量的字典,另一個是可調用對象,該對象使用HTTP狀態碼和會返回給客戶端的HTTP頭來初始化響應
environ 變量包含一些熟悉的環境變量,如HTTP_HOST,HTTP_USER_AGENT,REMOTE_ADDR,REQUEST_METHOD,SERVER_PORT,部分如下:
Hello world! GATEWAY_INTERFACE = ‘CGI/1.1‘ HTTP_ACCEPT = ‘text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8‘ HTTP_ACCEPT_ENCODING = ‘gzip, deflate, br‘ HTTP_ACCEPT_LANGUAGE = ‘zh-CN,zh;q=0.9,en;q=0.8‘ HTTP_CONNECTION = ‘keep-alive‘ HTTP_HOST = ‘127.0.0.1:9999‘ HTTP_USER_AGENT = ‘Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36‘ QUERY_STRING = ‘‘ REMOTE_ADDR = ‘127.0.0.1‘ REQUEST_METHOD = ‘GET‘ SERVER_PORT = ‘9999‘ SERVER_PROTOCOL = ‘HTTP/1.1‘ SERVER_SOFTWARE = ‘WSGIServer/0.2‘
3、這個可調用對象必須返回一個可叠代對象用於組成響應
wsgiref參考庫中有以下幾個子模塊:
* util -- 一些有用的功能和包裝
* headers -- 管理響應頭
* handlers -- 為server/gateway實現如何處理的基類
* simple_server -- 實現一個簡單的WSGI HTTP服務器
* validate -- 位於應用程序和server之間檢測錯誤的校驗包裝
二、WSGI HTTP Server端的使用
1. 啟動一個簡單的WSGI HTTP Server:
# 簡單web 1 from wsgiref.simple_server import make_server def demo_app(environ, start_response): #copy自simple_server模塊 from io import StringIO stdout = StringIO() print("Hello world!", file=stdout) print(file=stdout) h = sorted(environ.items()) for k, v in h: print(k, ‘=‘, repr(v), file=stdout) start_response("200 OK", [(‘Content-Type‘, ‘text/plain; charset=utf-8‘)]) return [stdout.getvalue().encode("utf-8")] ip = ‘127.0.0.1‘ port = 9999 server = make_server(ip, port, demo_app) server.serve_forever() server.server_close()
wsgiref.simple_server.make_server(host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler)
啟動一個WSGI服務器,必須傳入host, port, app三個參數。
在運行這段程序之後,就已經實現了一個監聽在9999端口的webServer,下面是服務端運行狀態和瀏覽器中訪問結果:
訪問 http://127.0.0.1:9999/ #server端運行狀態: 127.0.0.1 - - [26/Dec/2017 15:01:13] "GET / HTTP/1.1" 200 2128 127.0.0.1 - - [26/Dec/2017 15:01:13] "GET /favicon.ico HTTP/1.1" 200 2096 #瀏覽器訪問結果: Hello world! Apple_PubSub_Socket_Render = ‘/private/tmp/com.apple.launchd.Gx10g4snot/Render‘ CLICOLOR = ‘1‘ CONTENT_LENGTH = ‘‘ CONTENT_TYPE = ‘text/plain‘ GATEWAY_INTERFACE = ‘CGI/1.1‘ GREP_OPTIONS = ‘--color=auto‘ HOME = ‘/Users/ihoney‘ HTTP_ACCEPT = ‘text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8‘ HTTP_ACCEPT_ENCODING = ‘gzip, deflate, br‘ HTTP_ACCEPT_LANGUAGE = ‘zh-CN,zh;q=0.9,en;q=0.8‘ HTTP_CONNECTION = ‘keep-alive‘ HTTP_HOST = ‘127.0.0.1:9999‘ HTTP_UPGRADE_INSECURE_REQUESTS = ‘1‘ HTTP_USER_AGENT = ‘Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36‘ LC_CTYPE = ‘zh_CN.UTF-8‘ ......
2. 自定義響應的網頁內容:
# 簡單web 2 from wsgiref.simple_server import make_server def application(environ:dict,start_response): # print(type(environ),environ) html = "<h1>北京歡迎你</h1>" # start_response("200 OK", [(‘Content-Type‘,‘text/plain; charset=utf-8‘)]) #文本格式 start_response("200 OK", [(‘Content-Type‘, ‘text/html; charset=utf-8‘)]) #html格式 return [html.encode()] ip = ‘127.0.0.1‘ port =9999 server = make_server(ip,port,application) server.serve_forever() server.server_close() #運行結果: 127.0.0.1 - - [26/Dec/2017 15:38:55] "GET / HTTP/1.1" 200 24 127.0.0.1 - - [26/Dec/2017 15:38:55] "GET /favicon.ico HTTP/1.1" 200 24
瀏覽器訪問結果:
simple_server 只是參考,不可用於生產環境。
三、QUERY_STRING 查詢字符串的解析
1. 使用cgi模塊:
# 簡單web 3 使用cgi模塊解析query_string import cgi from wsgiref.simple_server import make_server def application(environ:dict,start_response): qstr = environ.get("QUERY_STRING") print(qstr) # ?id=5&name=ihoney&age=18,19 print(cgi.parse_qs(qstr)) #字典,value為列表類型 print(cgi.parse_qsl(qstr)) #二元組列表 html = "<h1>北京歡迎你</h1>" start_response("200 OK", [(‘Content-Type‘, ‘text/html; charset=utf-8‘)]) return [html.encode()] ip = ‘127.0.0.1‘ port =9999 server = make_server(ip,port,application) server.serve_forever() server.server_close() #瀏覽器訪問http://127.0.0.1:9999/?id=5&name=ihoney&age=18,19 #運行結果: 127.0.0.1 - - [26/Dec/2017 15:51:17] "GET /?id=5&name=ihoney&age=18,19 HTTP/1.1" 200 24 id=5&name=ihoney&age=18,19 {‘age‘: [‘18,19‘], ‘name‘: [‘ihoney‘], ‘id‘: [‘5‘]} [(‘id‘, ‘5‘), (‘name‘, ‘ihoney‘), (‘age‘, ‘18,19‘)]
在寫的時候IDE工具就會提示CGI模塊已經過期了,建議使用urllib庫。
2. 使用urllib庫
# 簡單web 4 使用urllib模塊解析query_string from urllib import parse from wsgiref.simple_server import make_server def application(environ:dict,start_response): qstr = environ.get("QUERY_STRING") print(qstr) # ?id=5&name=ihoney&age=18,19 print(parse.parse_qs(qstr)) #字典,value為列表類型 print(parse.parse_qsl(qstr)) #二元組列表 html = "<h1>北京歡迎你</h1>" start_response("200 OK", [(‘Content-Type‘, ‘text/html; charset=utf-8‘)]) return [html.encode()] ip = ‘127.0.0.1‘ port =9999 server = make_server(ip,port,application) server.serve_forever() server.server_close() #瀏覽器訪問:http://127.0.0.1:9999/?id=5&name=ihoney&age=18,19 #運行結果: id=5&name=ihoney&age=18,19 {‘id‘: [‘5‘], ‘age‘: [‘18,19‘], ‘name‘: [‘ihoney‘]} [(‘id‘, ‘5‘), (‘name‘, ‘ihoney‘), (‘age‘, ‘18,19‘)] 127.0.0.1 - - [26/Dec/2017 15:58:40] "GET /?id=5&name=ihoney&age=18,19 HTTP/1.1" 200 24
3. 使用第三方庫webob
pip3 install webob
第三方庫webob可以把環境數據的解析封裝成對象,使用時直接調用。
3.1 webob.Request
#簡單web 5,使用第三方庫webob解析 from wsgiref.simple_server import make_server from webob import Request, Response def application(environ: dict, start_response): request = Request(environ) print(request.method) print(request.path) print(request.GET) print(request.POST) print(request.params) print(request.query_string) html = "<h1>北京歡迎你</h1>" start_response("200 OK", [(‘Content-Type‘, ‘text/html; charset=utf-8‘)]) return [html.encode()] ip = ‘127.0.0.1‘ port = 9999 server = make_server(ip, port, application) server.serve_forever() server.server_close() #瀏覽器訪問:http://127.0.0.1:9999/index.html?id=5&name=tom,jerry&age=17&age=18,19 #運行結果: GET /index.html GET([(‘id‘, ‘5‘), (‘name‘, ‘tom,jerry‘), (‘age‘, ‘17‘), (‘age‘, ‘18,19‘)]) <NoVars: Not a form request> NestedMultiDict([(‘id‘, ‘5‘), (‘name‘, ‘tom,jerry‘), (‘age‘, ‘17‘), (‘age‘, ‘18,19‘)]) id=5&name=tom,jerry&age=17&age=18,19 127.0.0.1 - - [26/Dec/2017 16:51:41] "GET /index.html?id=5&name=tom,jerry&age=17&age=18,19 HTTP/1.1" 200 24
3.2 webob.Resphone
# from wsgiref.simple_server import make_server from webob import Request, Response def application(environ: dict, start_response): res = Response("<h1>北京歡迎你</h1>") return res(environ,start_response) #__call__ ip = ‘127.0.0.1‘ port = 9999 server = make_server(ip, port, application) server.serve_forever() server.server_close() #瀏覽器訪問:http://127.0.0.1:9999/index.html?id=5&name=tom,jerry&age=17&age=18,19 #運行結果: 127.0.0.1 - - [26/Dec/2017 18:08:03] "GET /index.html?id=5&name=tom,jerry&age=17&age=18,19 HTTP/1.1" 200 24
3.3 MultiDict
Request.GET、Request.POST 都是MultiDict字典
# multidict from webob.multidict import MultiDict md = MultiDict() md[1] = ‘b‘ md.add(1,‘a‘) print(md.get(1)) #只返回一個值 print(md.getall(1)) # print(md.getone(1)) #要求key的value只能有一個,否則拋KeyError異常 print(md.get(‘c‘)) #不存在返回默認值None #運行結果: a [‘b‘, ‘a‘] None
3.4 webob.dec.wsgify 裝飾器
將一個函數變成一個WSGI應用程序
# wsgify from wsgiref.simple_server import make_server from webob import Request, Response,dec def application(environ: dict, start_response): res = Response("<h1>北京歡迎你</h1>") #200 OK #[(‘Content-Type‘, ‘text/html; charset=UTF-8‘), (‘Content-Length‘, ‘0‘)] return res(environ,start_response) #__call__ @dec.wsgify def app(request:Request) -> Response: return Response("<h1>Welcome to BeiJing</h1>") ip = ‘127.0.0.1‘ port = 9999 # server = make_server(ip, port, application) server = make_server(ip, port, app) server.serve_forever() server.server_close() #瀏覽器訪問:http://127.0.0.1:9999/index.html?id=5&name=tom,jerry&age=17&age=18,19 #運行結果: 127.0.0.1 - - [26/Dec/2017 20:25:14] "GET /index.html?id=5&name=tom,jerry&age=17&age=18,19 HTTP/1.1" 200 27 127.0.0.1 - - [26/Dec/2017 20:25:14] "GET /favicon.ico HTTP/1.1" 200 27
改進:
from wsgiref.simple_server import make_server from webob import Request, Response,dec @dec.wsgify def app(request:Request) -> Response: return Response("<h1>Welcome to BeiJing.</h1>") if __name__ == "__main__": ip = ‘127.0.0.1‘ port = 9999 server = make_server(ip, port, app) try: server.serve_forever() except KeyBoardInterrupt: pass finally: server.server_close()
總結:
本文簡單介紹了WSGI、WSGI HTTP Server、查詢字符串的處理、第三方庫webob的一些用法。
[Python WEB開發] 使用WSGI開發類Flask框架 (二)