Web服务器处理过程的第一步是Web服务器从客户端接到请求(即GET和POST请求),并调用相应的应用程序。它然后等待HTML页面,与此同时,客户端也在等待。一旦应用程序处理完成,它会将生成的动态HTML页面返回给服务器端,然后服务器端再将这个最终的结果返回给用户。对于表当的处理股过程,服务器与外部应用程序交互,收到并将生成的HTML页面通过CGI返回给客户端。
客户端输入给Web服务器端的表单可能包括处理过程和一些存储在后台数据库中的表单。需要记住的是,含有需要用户输入项(如文本框、单选按钮)、Submit按钮、图片的Web页面,都会涉及某种CGI活动。
由于CGI有明显的局限性,以及限制Web服务器同时处理客户端的数量,因此在一般的生产环境的Web应用中不会使用CGI。
CGI应用程序和典型的应用程序有些不同,主要区别在于输入、输出以及用户和程序交互方面。当一个CGI脚本启动后,需要获取用户提供的表单数据,但这些数据必须从Web客户端才可以获得,而不是从服务器或者硬盘上获得,这就是请求(request)。
与标准输出不同,这些输出将会发送回连接的Web客户端,而不是发送到屏幕、GUI窗口或者硬盘上。这些返回的数据必须是具有一系列有效头文件的HTML标签数据。如果Web客户端是浏览器,由于浏览器只能识别有效的HTTP数据,所以会发生错误。
CGI应用程序在用户和脚本之间没有任何交互,所有的交互都将发生在Web客户端(基于用户的行为)、Web服务器端和CGI应用程序间。
CGI方式无法进行拓展,CGI进程(有点类似Python解释器)针对每个请求进行创建,用完就抛弃。如果应用程序有成百上千个请求时,创建大量的语言解释器进程就会消耗大量的资源,从而导致服务器停止。针对该问题,有两种解决办法:一是服务器集成,二是外部进程。
服务器集成,也称服务器API。当前最广泛的服务器解决方案是Apache HTTP Web服务器,这是一个开源的解决方案,通常称为Apache,拥有一个服务器API。另外使用术语模块来描述服务器上的插入的编译后的组件,这些组件可以拓展服务器的功能和用途。服务器一般还会提供压缩数据、安全、代理、虚拟主机等功能。
外部进程,这是让CGI应用在服务器外部运行。当有请求进入时,服务器将这个请求传递到外部进程中。这种方式的可拓展性比纯CGI要好,,因为外部进程存在的时间很长,而不是处理完当个请求后就终止。使用外部进程最广为人知的解决方案时FastCGI。
由于外部进程使用了不同的调用机制,所以开发者有了新的负担。不仅要开发应用本身,还要决定与Web服务器的集成。这就限制了用户的灵活性,这就很不python了。
这就导致了Web服务器网类接口(WSGI)标准的建立了。
WSGI这不是一个服务器,也不是用于与程序交互的API,更不是真实的代码,而只是定义的一个接口,WSGI规范用于处理日益增多的不同的Web框架、Web服务器,及其他调用方式(如纯CGI、服务器API、外部进程)。
WSGI的目标是在Web服务器和Web框架层之间提供一个通用的API标准,减少之间的互操性并形成统一的调用方式。
看下面WSGI简单示例:
from wsgiref.simple_server import make_server
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [b'Hello, web!
']
httpd = make_server('', 8080, application)
print('Serving HTTP on port 8000...')
# 开始监听HTTP请求:
httpd.serve_forever()
根据WSGI定义,其应用是可调用的对象,其参数固定为以下两个:一个是含有服务器环境变量的字典,另一个是可调用对象,该对象使用HTTP状态码和会返回给客户端的HTTP头来初始化响应。
整个application()函数本身没有涉及到任何解析HTTP的部分,也就是说,底层代码不需要我们自己编写,
我们只负责在更高层次上考虑如何响应请求就可以了。
application()函数必须由WSGI服务器来调用。有很多符合WSGI规范的服务器,我们可以挑选一个来用。
Python内置了一个WSGI服务器,这个模块叫wsgiref
application()函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数:
//environ:一个包含所有HTTP请求信息的dict对象;
//start_response:一个发送HTTP响应的函数。
在application()函数中,调用:
start_response('200 OK', [('Content-Type', 'text/html')])
就发送了HTTP响应的Header,注意Header只能发送一次,也就是只能调用一次start_response()函数。
start_response()函数接收两个参数,一个是HTTP响应码,一个是一组list表示的HTTP Header,每
个Header用一个包含两个str的tuple表示。
通常情况下,都应该把Content-Type头发送给浏览器。其他很多常用的HTTP Header也应该发送。
然后,函数的返回值b'
将作为HTTP响应的Body发送给浏览器。Hello, web!
'
有了WSGI,我们关心的就是如何从environ这个dict对象拿到HTTP请求信息,然后构造HTML,
通过start_response()发送Header,最后返回Body。
WSGI协议主要包括server和application两部分:
WSGI协议其实是定义了一种server与application解耦的规范,即可以有多个实现WSGI server的服务器,也可以有多个实现WSGI application的框架,那么就可以选择任意的server和application组合实现自己的web应用。例如uWSGI和Gunicorn都是实现了WSGI server协议的服务器,Django,Flask是实现了WSGI application协议的web框架,可以根据项目实际情况搭配使用。
示例一:
from wsgiref.simple_server import make_server
def f1():
f1=open("index1.html","rb")
data1=f1.read()
return [data1]
def f2():
f2=open("index2.html","rb")
data2=f2.read()
return [data2]
def application(environ, start_response):
print(environ['PATH_INFO'])
path=environ['PATH_INFO']
start_response('200 OK', [('Content-Type', 'text/html')])
if path=="/yuan":
return f1()
elif path=="/alex":
return f2()
else:
return ["404
".encode("utf8")]
httpd = make_server('', 8502, application)
print('Serving HTTP on port 8084...')
# 开始监听HTTP请求:
httpd.serve_forever()
示例二:
from wsgiref.simple_server import make_server
def f1(req):
print(req)
print(req["QUERY_STRING"])
f1=open("index1.html","rb")
data1=f1.read()
return [data1]
def f2(req):
f2=open("index2.html","rb")
data2=f2.read()
return [data2]
import time
def f3(req): #模版以及数据库
f3=open("index3.html","rb")
data3=f3.read()
times=time.strftime("%Y-%m-%d %X", time.localtime())
data3=str(data3,"utf8").replace("!time!",str(times))
return [data3.encode("utf8")]
def routers():
urlpatterns = (
('/yuan',f1),
('/alex',f2),
("/cur_time",f3)
)
return urlpatterns
def application(environ, start_response):
print(environ['PATH_INFO'])
path=environ['PATH_INFO']
start_response('200 OK', [('Content-Type', 'text/html')])
urlpatterns = routers()
func = None
for item in urlpatterns:
if item[0] == path:
func = item[1]
break
if func:
return func(environ)
else:
return ["404
".encode("utf8")]
httpd = make_server('', 8518, application)
print('Serving HTTP on port 8084...')
# 开始监听HTTP请求:
httpd.serve_forever()