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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。