Web服务器网关接口(WSGI),又叫Python Web服务器网关接口(Python Web Server Gateway Interface),是用来描述web服务器与网站应用程序通信和应用程序如何处理请求的规范。它的第一个版本于2003年发布,从那时起,wsgi就成为了python web应用的开发标准,目前最新的版本是v1.0.1,于2010年9月26日发布。
使用python做过web开发的对下面的例子应该非常熟悉,运行之后访问则会显示出Hello,web!
from wsgiref.simple_server import make_server
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return '<h1>Hello, web!</h1>'
httpd = make_server('', 80, application)
print "Serving HTTP on port 80..."
httpd.serve_forever()
在上面的例子中,几行代码就可以搭建出一个HTTP服务器,并没有使用像apache,nginx一样的专用web服务器,而是使用wsgiref中的模块。对于这个服务来说,application是用户层的代码,而wsgi对内提供应用接口,对外充当web服务器。

也就是说,wsgi只是提供了一个管道,最终用来处理终端用户请求的还是用户层的代码,这里就有两个问题:
1,如何把用户请求交给application程序来处理?
2,如何把用户的请求数据等信息一并交给application?
wsgi的处理方式很简单巧妙:为每个请求注册一个回调函数,这个回调函数接受两个参数,第一个参数为用户和环境数据,第二个为HTTP头部返回函数。由于这个回调函数是用户层程序,就很容易通过参数的形式获取前端的请求数据等信息:
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return "Your IP Addr is %s, Your Request Path is %s"%(environ['REMOTE_ADDR'],environ['PATH_INFO'][1:] or '/')
from wsgiref.simple_server import make_server
httpd = make_server('', 80, application)
print "Serving HTTP on port 80..."
httpd.serve_forever()
这样一来,网站应用程序就可以通过wsgi传入的environ参数获取用户输入,从而给不同的用户返回不同的内容。
实现一个简单的wsgi服务器
只有能动手实现一个wsgi服务器,才能对它有深入的认识,废话不多说,直接上代码:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# 对于wsgi的理解并对其进行实验
# https://www.python.org/dev/peps/pep-3333/
import socket,re,os
httpre=re.compile(r"^(\w*)\s*([^\s]*)\s*HTTP\/([.\d]*)[\r\n]*([\s\S]+)[\r\n]*[\r\n]*([\s\S]?)$")
headerre=re.compile(r"([^\s]*):\s*([^\r\n]*)[\r\n]")
def recvall(link,buflen):
buf = link.recv(buflen);
if len(buf) < buflen:
return buf
else:
return "%s%s"%(buf,recvall(link,buflen))
def headerdic(str):
mydic = {}
matches = headerre.findall(str)
for line in matches:
mydic[line[0]]=line[1]
return mydic
#http request return
#method, uri, httpversion, header, body
def httppareser(request):
try:
raw_re = httpre.findall(request.strip())[0]
return raw_re[0], raw_re[1], raw_re[2], raw_re[3], raw_re[4]
except:
return None
class WSGIServer:
def __init__(self,host="127.0.0.1",port=8080,keepalive=5):
self.host = host
self.port = port
self.keepalive = keepalive
self.environ = os.environ
def run(self,app):
sockt = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sockt.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sockt.bind((self.host,self.port))
sockt.listen(5)
while True:
con,addr = sockt.accept()
#con.settimeout(self.keepalive)
raw_request = recvall(con,1024)
method,uri,httpversion,header,body = httppareser(raw_request)
environ = header#,**os.environ)
global response_header
#start_response
#gen http response header
def start_response(status,response_header_list):
global response_header
headers = ""
for header in response_header_list:
headers = headers+"%s:%s\r\n"%(header[0],header[1])
response_header = "HTTP/1.1 %s\r\n%s\r\n"%(status,headers)
response_body = app(environ,start_response)
con.send(response_header+response_body)
con.close()
#This is the application
def app(environ,start_response):
start_response("404 Not Found",[("Content-Type","text/html"),("User-Agent","Mybody")])
return """<!DOCTYPE HTML>
<html>
<head lang="zh-cn">
<meta charset="utf-8">
<title>你好世界</title>
<body>
<h1>Hello World</h1>
</body>
</html>"""
srv = WSGIServer()
srv.run(app)
当然,python对静态文件的处理效率还是比较低的,所以通常生产环境的做法是在wsgi前添加一个反向代理服务器,静态文件由服务器直接获取,动态请求则交给python来处理,如下:

我们注意到,在应用程序被调用的时候,wsgi给我们传递了两个参数,一个是组合的字典类型数据environ,另一个是可调用的函数,应用程序通过调用传入的函数来生成HTTP返回的头部信息,通过返回值生成HTTP的body信息。从网络传输层的角度来看,HTTP头部数据和Body数据是一样的数据,只是用两个”\r\n”分隔而已,何不直接返回头部和body信息,徒增一个函数不仅没有带来什么帮助,反而给开发带来更多麻烦。对此python开发了第二代web网关标准,应用程序只传入一个environ参数即可,相关介绍,可以访问PEP0444查看。
评论列表: