Python模組WSGI使用詳解,pythonwsgi使用詳解

來源:互聯網
上載者:User

Python模組WSGI使用詳解,pythonwsgi使用詳解

WSGI(Web Server Gateway Interface):Web服務網關介面,是Python中定義的伺服器程式和應用程式之間的介面。

Web程式開發中,一般分為伺服器程式和應用程式。伺服器程式負責對socket服務的資料進行封裝和整理,而應用程式則負責對Web請求進行邏輯處理。

Web應用本質上也是一個socket伺服器,使用者的瀏覽器就是一個socket用戶端。

我們先用socket編程實現一個簡單的Web伺服器:

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()函數就是伺服器函數,handle_request()就是應用程式。
下面我們再用python的wsgiref模組來實現跟上述代碼一樣的Web伺服器:

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

上面兩份代碼實現的效果是一樣的,調用wsgiref模組則明顯節省了代碼量,是整個程式更加簡潔。
wsgiref模組封裝了socket服務端的代碼,只留下一個調用的介面,省去了程式員的麻煩,程式員可以將精力放在Web請求的邏輯處理中。

以上述的代碼為例,詳細看一下wsgiref模組的源碼中一些關鍵的地方:

if __name__ == "__main__":   httpd = make_server("",8000,handle_request)   print("Serving http on port 80000")   httpd.serve_forever() 

1、整個程式的入口為make_server()函數:

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) #預設建立一個WSGIServer類   server.set_app(app) #將應用程式,即邏輯處理函數傳給類   return server 

2、make_server()函數預設產生一個WSGIServer類:

class WSGIServer(HTTPServer):
class HTTPServer(socketserver.TCPServer):
class TCPServer(BaseServer):

WSGIServer,HTTPServer兩個類沒有初始化函數,調用父類的初始化函數,TCPServer類的__init__()函數拓展了BaseServer

類的__init__()函數:

#BaseServer類的__init__()函數: 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__()函數: 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類的初始化函數還調用了server_bind(self),server_bind(self)兩個函數:

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()函數調用了socket.bind()函數,而server_activate()調用了socket.listen()函數:

3、server.set_app(app),此處傳入Web請求的處理邏輯:

def set_app(self,application):   self.application = application 

4、httpd.serve_forever()函數調用BaseServer類的_handle_request_noblock()函數處理多路請求:

def _handle_request_noblock(self):   try:     request, client_address = self.get_request() #get_request()調用了socket.accept()函數   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()調用socket.close()關閉socket      def finish_request(self, request, client_address):   """Finish one request by instantiating RequestHandlerClass."""   self.RequestHandlerClass(request, client_address, self) 

5、process_request()函數調用了finish_request()函數,簡介調用了make_server函數的預設參數WSGIRequestHandler類:

class WSGIRequestHandler(BaseHTTPRequestHandler):
class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
class StreamRequestHandler(BaseRequestHandler):

#調用BaseRequestHandler類的初始化函數: 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、初始化函數調用之後調用WSGIRequestHandler類的handle()函數擷取server的邏輯處理函數:

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()) #此處調用server的邏輯處理函數   finally:     stdout.detach() 

7、BaseHandler類的handler.run()函數執行邏輯處理:

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:一個包含所有HTTP請求資訊的dict對象
self.start_response:一個發送HTTP響應的函數。

在application函數中,調用:

res("200 OK",[("Content-Type","text/html")]) 

這樣就發送了HTTP響應的頭資訊

8、BaseHandler類的setup_environ()函數擷取HTTP請求的頭資訊:

def setup_environ(self):   """Set up the environment for one request"""   env = self.environ = self.os_environ.copy()    os_environ= read_environ()  read_environ()函數:  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()函數:

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 

以上就是本文的全部內容,希望對大家的學習有所協助,也希望大家多多支援幫客之家。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.