Python模块WSGI使用详解
WSGI(WebServerGatewayInterface):Web服务网关接口,是Python中定义的服务器程序和应用程序之间的接口。
Web程序开发中,一般分为服务器程序和应用程序。服务器程序负责对socket服务的数据进行封装和整理,而应用程序则负责对Web请求进行逻辑处理。
Web应用本质上也是一个socket服务器,用户的浏览器就是一个socket客户端。
我们先用socket编程实现一个简单的Web服务器:
importsocket defhandle_request(client): buf=client.recv(1024) print(buf) msg="HTTP/1.1200OK\r\n\r\n"#HTTP头信息 client.send(('%s'%msg).encode()) msg="Hello,World!" client.send(('%s'%msg).encode()) defmain(): ip_port=("localhost",8000) sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM) sock.bind(ip_port) sock.listen(5) whileTrue: conn,addr=sock.accept() handle_request(conn) conn.close() if__name__=="__main__": main()
上述代码中,main()函数就是服务器函数,handle_request()就是应用程序。
下面我们再用python的wsgiref模块来实现跟上述代码一样的Web服务器:
fromwsgiref.simple_serverimportmake_server defhandle_request(env,res): res("200OK",[("Content-Type","text/html")]) body="HelloWorld!
" return[body.encode("utf-8")] if__name__=="__main__": httpd=make_server("",8000,handle_request) print("Servinghttponport80000") httpd.serve_forever()
上面两份代码实现的效果是一样的,调用wsgiref模块则明显节省了代码量,是整个程序更加简洁。
wsgiref模块封装了socket服务端的代码,只留下一个调用的接口,省去了程序员的麻烦,程序员可以将精力放在Web请求的逻辑处理中。
以上述的代码为例,详细看一下wsgiref模块的源码中一些关键的地方:
if__name__=="__main__": httpd=make_server("",8000,handle_request) print("Servinghttponport80000") httpd.serve_forever()
1、整个程序的入口为make_server()函数:
defmake_server(host,port,app,server_class=WSGIServer,handler_class=WSGIRequestHandler): """CreateanewWSGIserverlisteningon`host`and`port`for`app`""" server=server_class((host,port),handler_class)#默认创建一个WSGIServer类 server.set_app(app)#将应用程序,即逻辑处理函数传给类 returnserver
2、make_server()函数默认生成一个WSGIServer类:
classWSGIServer(HTTPServer):
classHTTPServer(socketserver.TCPServer):
classTCPServer(BaseServer):
WSGIServer,HTTPServer两个类没有初始化函数,调用父类的初始化函数,TCPServer类的__init__()函数拓展了BaseServer
类的__init__()函数:
#BaseServer类的__init__()函数: def__init__(self,server_address,RequestHandlerClass): """Constructor.Maybeextended,donotoverride.""" 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.Maybeextended,donotoverride.""" BaseServer.__init__(self,server_address,RequestHandlerClass) self.socket=socket.socket(self.address_family,self.socket_type) ifbind_and_activate: try: self.server_bind() self.server_activate() except: self.server_close() raise
TCPServer类的初始化函数还调用了server_bind(self),server_bind(self)两个函数:
defserver_bind(self): """Calledbyconstructortobindthesocket.Maybeoverridden.""" ifself.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() defself.server_activate(self): """Calledbyconstructortoactivatetheserver.Maybeoverridden.""" self.socket.listen(self.request_queue_size)
可以看到server.bind()函数调用了socket.bind()函数,而server_activate()调用了socket.listen()函数:
3、server.set_app(app),此处传入Web请求的处理逻辑:
defset_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()函数 exceptOSError: return ifself.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)
defprocess_request(self,request,client_address): self.finish_request(request,client_address) self.shutdown_request(request)#shutdown_request()调用socket.close()关闭socket deffinish_request(self,request,client_address): """FinishonerequestbyinstantiatingRequestHandlerClass.""" self.RequestHandlerClass(request,client_address,self)
5、process_request()函数调用了finish_request()函数,简介调用了make_server函数的默认参数WSGIRequestHandler类:
classWSGIRequestHandler(BaseHTTPRequestHandler):
classBaseHTTPRequestHandler(socketserver.StreamRequestHandler):
classStreamRequestHandler(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的逻辑处理函数:
defhandle(self): """HandleasingleHTTPrequest""" try: handler=ServerHandler(self.rfile,stdout,self.get_stderr(),self.get_environ()) handler.request_handler=self#backpointerforlogging handler.run(self.server.get_app())#此处调用server的逻辑处理函数 finally: stdout.detach()
7、BaseHandler类的handler.run()函数执行逻辑处理:
defrun(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#...andlettheactualserverfigureitout.
self.environ:一个包含所有HTTP请求信息的dict对象
self.start_response:一个发送HTTP响应的函数。
在application函数中,调用:
res("200OK",[("Content-Type","text/html")])
这样就发送了HTTP响应的头信息
8、BaseHandler类的setup_environ()函数获取HTTP请求的头信息:
defsetup_environ(self): """Setuptheenvironmentforonerequest""" env=self.environ=self.os_environ.copy() os_environ=read_environ() read_environ()函数: defread_environ(): """Readenvironment,fixingHTTPvariables""" enc=sys.getfilesystemencoding() esc='surrogateescape' try: ''.encode('utf-8',esc) exceptLookupError: esc='replace' environ={} #Takethebasicenvironmentfromnative-unicodeos.environ.Attemptto #fixupthevariablesthatcomefromtheHTTPrequesttocompensatefor #thebytes->unicodedecodingstepthatwillalreadyhavetakenplace. fork,vinos.environ.items(): if_needs_transcode(k): #Onwin32,theos.environisnativelyUnicode.Differentservers #decodetherequestbytesusingdifferentencodings. ifsys.platform=='win32': software=os.environ.get('SERVER_SOFTWARE','').lower() #OnIIS,theHTTPrequestwillbedecodedasUTF-8aslong #astheinputisavalidUTF-8sequence.Otherwiseitis #decodedusingthesystemcodepage(mbcs),withnowayto #detectthishashappened.BecauseUTF-8isthemorelikely #encoding,andmbcsisinherentlyunreliable(anmbcsstring #thathappenstobevalidUTF-8willnotbedecodedasmbcs) #alwaysrecreatetheoriginalbytesasUTF-8. ifsoftware.startswith('microsoft-iis/'): v=v.encode('utf-8').decode('iso-8859-1') #Apachemod_cgiwritesbytes-as-unicode(asifISO-8859-1)direct #totheUnicodeenviron.Nomodificationneeded. elifsoftware.startswith('apache/'): pass #Python3'shttp.server.CGIHTTPRequestHandlerdecodes #usingtheurllib.unquotedefaultofUTF-8,amongstother #issues. elif( software.startswith('simplehttp/') and'python/3'insoftware ): v=v.encode('utf-8').decode('iso-8859-1') #Forotherservers,guessthattheyhavewrittenbytesto #theenvironusingstdiobyte-orientedinterfaces,endingup #withthesystemcodepage. else: v=v.encode(enc,'replace').decode('iso-8859-1') #Recoverbytesfromunicodeenviron,usingsurrogateescapes #whereavailable(Python3.1+). else: v=v.encode(enc,esc).decode('iso-8859-1') environ[k]=v returnenviron
9、BaseHandler类的start_response()函数:
defstart_response(self,status,headers,exc_info=None): """'start_response()'callableasspecifiedbyPEP3333""" ifexc_info: try: ifself.headers_sent: #Re-raiseoriginalexceptionifheaderssent raiseexc_info[0](exc_info[1]).with_traceback(exc_info[2]) finally: exc_info=None#avoiddanglingcircularref elifself.headersisnotNone: raiseAssertionError("Headersalreadyset!") self.status=status self.headers=self.headers_class(headers) status=self._convert_string_type(status,"Status") assertlen(status)>=4,"Statusmustbeatleast4characters" assertstatus[:3].isdigit(),"Statusmessagemustbeginw/3-digitcode" assertstatus[3]=="","Statusmessagemusthaveaspaceaftercode" if__debug__: forname,valinheaders: name=self._convert_string_type(name,"Headername") val=self._convert_string_type(val,"Headervalue") returnself.write
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。