Python模塊WSGI使用詳解
WSGI(Web Server Gateway Interface):Web服務(wù)網(wǎng)關(guān)接口,是Python中定義的服務(wù)器程序和應(yīng)用程序之間的接口。
Web程序開(kāi)發(fā)中,一般分為服務(wù)器程序和應(yīng)用程序。服務(wù)器程序負(fù)責(zé)對(duì)socket服務(wù)的數(shù)據(jù)進(jìn)行封裝和整理,而應(yīng)用程序則負(fù)責(zé)對(duì)Web請(qǐng)求進(jìn)行邏輯處理。
Web應(yīng)用本質(zhì)上也是一個(gè)socket服務(wù)器,用戶的瀏覽器就是一個(gè)socket客戶端。
我們先用socket編程實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Web服務(wù)器:
import socket
def handle_request(client):
buf = client.recv(1024)
print(buf)
msg = "HTTP/1.1 200 OK\r\n\r\n" #HTTP頭信息
client.send(('%s' % msg).encode())
msg = "Hello, World!"
client.send(('%s' % msg).encode())
def main():
ip_port = ("localhost", 8000)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(ip_port)
sock.listen(5)
while True:
conn, addr = sock.accept()
handle_request(conn)
conn.close()
if __name__ == "__main__":
main()
上述代碼中,main()函數(shù)就是服務(wù)器函數(shù),handle_request()就是應(yīng)用程序。
下面我們?cè)儆胮ython的wsgiref模塊來(lái)實(shí)現(xiàn)跟上述代碼一樣的Web服務(wù)器:
from wsgiref.simple_server import make_server
def handle_request(env, res):
res("200 OK",[("Content-Type","text/html")])
body = "<h1>Hello World!</h1>"
return [body.encode("utf-8")]
if __name__ == "__main__":
httpd = make_server("",8000,handle_request)
print("Serving http on port 80000")
httpd.serve_forever()
上面兩份代碼實(shí)現(xiàn)的效果是一樣的,調(diào)用wsgiref模塊則明顯節(jié)省了代碼量,是整個(gè)程序更加簡(jiǎn)潔。
wsgiref模塊封裝了socket服務(wù)端的代碼,只留下一個(gè)調(diào)用的接口,省去了程序員的麻煩,程序員可以將精力放在Web請(qǐng)求的邏輯處理中。
以上述的代碼為例,詳細(xì)看一下wsgiref模塊的源碼中一些關(guān)鍵的地方:
if __name__ == "__main__":
httpd = make_server("",8000,handle_request)
print("Serving http on port 80000")
httpd.serve_forever()
1、整個(gè)程序的入口為make_server()函數(shù):
def make_server(host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler): """Create a new WSGI server listening on `host` and `port` for `app`""" server = server_class((host, port), handler_class) #默認(rèn)創(chuàng)建一個(gè)WSGIServer類 server.set_app(app) #將應(yīng)用程序,即邏輯處理函數(shù)傳給類 return server
2、make_server()函數(shù)默認(rèn)生成一個(gè)WSGIServer類:
class WSGIServer(HTTPServer):
class HTTPServer(socketserver.TCPServer):
class TCPServer(BaseServer):
WSGIServer,HTTPServer兩個(gè)類沒(méi)有初始化函數(shù),調(diào)用父類的初始化函數(shù),TCPServer類的__init__()函數(shù)拓展了BaseServer
類的__init__()函數(shù):
#BaseServer類的__init__()函數(shù): def __init__(self, server_address, RequestHandlerClass): """Constructor. May be extended, do not override.""" self.server_address = server_address self.RequestHandlerClass = RequestHandlerClass self.__is_shut_down = threading.Event() self.__shutdown_request = False
#TCPServer類的__init__()函數(shù):
def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
"""Constructor. May be extended, do not override."""
BaseServer.__init__(self, server_address, RequestHandlerClass)
self.socket = socket.socket(self.address_family,self.socket_type)
if bind_and_activate:
try:
self.server_bind()
self.server_activate()
except:
self.server_close()
raise
TCPServer類的初始化函數(shù)還調(diào)用了server_bind(self),server_bind(self)兩個(gè)函數(shù):
def server_bind(self):
"""Called by constructor to bind the socket.May be overridden."""
if self.allow_reuse_address:
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind(self.server_address)
self.server_address = self.socket.getsockname()
def self.server_activate(self):
"""Called by constructor to activate the server.May be overridden."""
self.socket.listen(self.request_queue_size)
可以看到server.bind()函數(shù)調(diào)用了socket.bind()函數(shù),而server_activate()調(diào)用了socket.listen()函數(shù):
3、server.set_app(app),此處傳入Web請(qǐng)求的處理邏輯:
def set_app(self,application): self.application = application
4、httpd.serve_forever()函數(shù)調(diào)用BaseServer類的_handle_request_noblock()函數(shù)處理多路請(qǐng)求:
def _handle_request_noblock(self):
try:
request, client_address = self.get_request() #get_request()調(diào)用了socket.accept()函數(shù)
except OSError:
return
if self.verify_request(request, client_address):
try:
self.process_request(request, client_address)
except:
self.handle_error(request, client_address)
self.shutdown_request(request)
else:
self.shutdown_request(request)
def process_request(self, request, client_address):
self.finish_request(request, client_address)
self.shutdown_request(request)#shutdown_request()調(diào)用socket.close()關(guān)閉socket
def finish_request(self, request, client_address):
"""Finish one request by instantiating RequestHandlerClass."""
self.RequestHandlerClass(request, client_address, self)
5、process_request()函數(shù)調(diào)用了finish_request()函數(shù),簡(jiǎn)介調(diào)用了make_server函數(shù)的默認(rèn)參數(shù)WSGIRequestHandler類:
class WSGIRequestHandler(BaseHTTPRequestHandler):
class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
class StreamRequestHandler(BaseRequestHandler):
#調(diào)用BaseRequestHandler類的初始化函數(shù):
def __init__(self, request, client_address, server):
self.request = request
self.client_address = client_address
self.server = server
self.setup()
try:
self.handle()
finally:
self.finish()
6、初始化函數(shù)調(diào)用之后調(diào)用WSGIRequestHandler類的handle()函數(shù)獲取server的邏輯處理函數(shù):
def handle(self):
"""Handle a single HTTP request"""
try:
handler = ServerHandler(self.rfile, stdout, self.get_stderr(), self.get_environ())
handler.request_handler = self # backpointer for logging
handler.run(self.server.get_app()) #此處調(diào)用server的邏輯處理函數(shù)
finally:
stdout.detach()
7、BaseHandler類的handler.run()函數(shù)執(zhí)行邏輯處理:
def run(self, application):
try:
self.setup_environ()
self.result = application(self.environ, self.start_response)
self.finish_response()
except:
try:
self.handle_error()
except:
self.close()
raise # ...and let the actual server figure it out.
self.environ:一個(gè)包含所有HTTP請(qǐng)求信息的dict對(duì)象
self.start_response:一個(gè)發(fā)送HTTP響應(yīng)的函數(shù)。
在application函數(shù)中,調(diào)用:
res("200 OK",[("Content-Type","text/html")])
這樣就發(fā)送了HTTP響應(yīng)的頭信息
8、BaseHandler類的setup_environ()函數(shù)獲取HTTP請(qǐng)求的頭信息:
def setup_environ(self):
"""Set up the environment for one request"""
env = self.environ = self.os_environ.copy()
os_environ= read_environ()
read_environ()函數(shù):
def read_environ():
"""Read environment, fixing HTTP variables"""
enc = sys.getfilesystemencoding()
esc = 'surrogateescape'
try:
''.encode('utf-8', esc)
except LookupError:
esc = 'replace'
environ = {}
# Take the basic environment from native-unicode os.environ. Attempt to
# fix up the variables that come from the HTTP request to compensate for
# the bytes->unicode decoding step that will already have taken place.
for k, v in os.environ.items():
if _needs_transcode(k):
# On win32, the os.environ is natively Unicode. Different servers
# decode the request bytes using different encodings.
if sys.platform == 'win32':
software = os.environ.get('SERVER_SOFTWARE', '').lower()
# On IIS, the HTTP request will be decoded as UTF-8 as long
# as the input is a valid UTF-8 sequence. Otherwise it is
# decoded using the system code page (mbcs), with no way to
# detect this has happened. Because UTF-8 is the more likely
# encoding, and mbcs is inherently unreliable (an mbcs string
# that happens to be valid UTF-8 will not be decoded as mbcs)
# always recreate the original bytes as UTF-8.
if software.startswith('microsoft-iis/'):
v = v.encode('utf-8').decode('iso-8859-1')
# Apache mod_cgi writes bytes-as-unicode (as if ISO-8859-1) direct
# to the Unicode environ. No modification needed.
elif software.startswith('apache/'):
pass
# Python 3's http.server.CGIHTTPRequestHandler decodes
# using the urllib.unquote default of UTF-8, amongst other
# issues.
elif (
software.startswith('simplehttp/')
and 'python/3' in software
):
v = v.encode('utf-8').decode('iso-8859-1')
# For other servers, guess that they have written bytes to
# the environ using stdio byte-oriented interfaces, ending up
# with the system code page.
else:
v = v.encode(enc, 'replace').decode('iso-8859-1')
# Recover bytes from unicode environ, using surrogate escapes
# where available (Python 3.1+).
else:
v = v.encode(enc, esc).decode('iso-8859-1')
environ[k] = v
return environ
9、BaseHandler類的start_response()函數(shù):
def start_response(self, status, headers,exc_info=None):
"""'start_response()' callable as specified by PEP 3333"""
if exc_info:
try:
if self.headers_sent:
# Re-raise original exception if headers sent
raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])
finally:
exc_info = None # avoid dangling circular ref
elif self.headers is not None:
raise AssertionError("Headers already set!")
self.status = status
self.headers = self.headers_class(headers)
status = self._convert_string_type(status, "Status")
assert len(status)>=4,"Status must be at least 4 characters"
assert status[:3].isdigit(), "Status message must begin w/3-digit code"
assert status[3]==" ", "Status message must have a space after code"
if __debug__:
for name, val in headers:
name = self._convert_string_type(name, "Header name")
val = self._convert_string_type(val, "Header value")
return self.write
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
python 3.6.7實(shí)現(xiàn)端口掃描器
這篇文章主要為大家詳細(xì)介紹了python 3.6.7實(shí)現(xiàn)端口掃描器,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09
Pyhton模塊和包相關(guān)知識(shí)總結(jié)
文中詳細(xì)整理了關(guān)于Python模塊和包的相關(guān)知識(shí)點(diǎn),剛?cè)腴T(mén)Python的小伙伴們可以學(xué)習(xí)一下,有助于加深Python基礎(chǔ)的理解.而且有詳細(xì)說(shuō)明及代碼示例,需要的朋友可以參考下2021-05-05
python 自動(dòng)監(jiān)控最新郵件并讀取的操作
這篇文章主要介紹了python 自動(dòng)監(jiān)控最新郵件并讀取的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-03-03
python將多個(gè)py文件和其他文件打包為exe可執(zhí)行文件
這篇文章主要介紹了python將多個(gè)py文件和其他文件打包為exe可執(zhí)行文件,通過(guò)準(zhǔn)備要打包的工程文件展開(kāi)詳情,需要的小伙伴可以參考一下2022-05-05
在python里創(chuàng)建一個(gè)任務(wù)(Task)實(shí)例
這篇文章主要介紹了在python里創(chuàng)建一個(gè)任務(wù)(Task)實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-04-04
python中@property和property函數(shù)常見(jiàn)使用方法示例
這篇文章主要介紹了python中@property和property函數(shù)常見(jiàn)使用方法,結(jié)合實(shí)例形式分析了Python @property和property函數(shù)功能、使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2019-10-10
pyecharts X軸標(biāo)簽太長(zhǎng)被截?cái)嗟膯?wèn)題及解決
這篇文章主要介紹了pyecharts X軸標(biāo)簽太長(zhǎng)被截?cái)嗟膯?wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11

