Python模块WSGI详解
来源:互联网 发布:json和ajax结合使用 编辑:程序博客网 时间:2024/05/02 17:24
WSGI(Web Server Gateway Interface):Web服务网关接口,是Python中定义的服务器程序和应用程序之间的接口。
Web程序开发中,一般分为服务器程序和应用程序。服务器程序负责对socket服务的数据进行封装和整理,而应用程序则负责对Web请求进行逻辑处理。
Web应用本质上也是一个socket服务器,用户的浏览器就是一个socket客户端。
我们先用socket编程实现一个简单的Web服务器:
import socketdef 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_serverdef 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 server2、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() raiseTCPServer类的初始化函数还调用了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 = application4、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()关闭socketdef 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 environ9、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详解
- Python-WSGI详解汇总
- Python WSGI详解
- python wsgi 详解浏览器请求过程
- python nginx+uwsgi+WSGI 处理请求详解
- python WSGI
- python +wsgi
- Python WSGI
- Python WSGI
- python WSGI
- wsgi详解
- 详解WSGI
- 非Python的Web服务器之Apache WSGI模块安装
- 捉摸Python的WSGI
- Python WSGI fastcgi
- Windows+Python+Apache+wsgi
- Linux+Python+Apahce+wsgi
- 转: Python WSGI fastcgi
- #1067 : 最近公共祖先·二
- 程序第一次启动从欢迎页跳到引导页,下次再启动直接从欢迎页跳转到主页
- ios编译linphone 开启x264编译
- <ROS> Ubuntu14.04下ROS indigo Eclipse开发环境搭建
- Error:(1, 0) Plugin with id ‘com.android.application’ not found
- Python模块WSGI详解
- M12D12WEB前端笔记(王)
- Spring RestTemplate: 比httpClient更优雅的Restful URL访问, java HttpPost with header,Spring RestTemplate在spr
- RCNN总结
- 工厂方法模式
- 练习:新浪微博资料修改页面
- [Git] 关于refs/for/ 和refs/heads/
- cocos2dx-3.2调用java(jni)来内嵌网页
- iOS 界面处理放在主线程中