python

超轻量级php框架startmvc

Python模块WSGI使用详解

更新时间:2020-05-20 17:42:01 作者:startmvc
WSGI(WebServerGatewayInterface):Web服务网关接口,是Python中定义的服务器程序和应用程序之间

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 

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

Python WSGI