大部分Openstack项目中的api模块都采用了wsgiref+ Paste Deployment的组合。 它们的目的很简单, 是将后端程序(application)提供的服务以WSGI的方式暴露给用户使用。
Paste Deployment是一个可以查找以及配置WSGI应用程序或者服务器的系统。 当正确的配置好WSGI服务之后,用户只需要调用一个简单的方法(loadapp)就可以使用WSGI的服务,而Paste也只需要应用程序的开发者提供应用程序的入口, 对用户屏蔽应用程序的实现细节。 总的来说,Paste Deployment可以使WSGI的部署变得简单。
wsgiref 是WSGI规范在python上的一个参考实现,它可以加到现有的web程序或者框架中以使其支持WSGI服务。它提供了一些工具来操作WSGI的环境变量、头文件等来实现WSGI应用程序。
为了能更好的理解这两个python框架,下面举一个简单的能运行的例子:
我们希望提供一个WSGI服务,目前只需要提供三个接口:
1. http://localhost:8001 返回默认的版本
2. http://localhost:8001/v1 返回当前访问的版本号1
3. http://localhost:8001/v2 返回当前访问的版本号2
同时我们希望用户在/v1和/v2 api的时候需要传入用户的认证信息,认证通过才能继续访问api。
根据这个功能信息我们将paste deployment配置文件api-paste.ini如下:
[composite:main]
use = egg:Paste#urlmap
/: mainversions
/v1: main_api_v1
/v2: main_api_v2
[pipeline:main_api_v1]
pipeline = auth main_api_app_v1
[pipeline:main_api_v2]
pipeline = auth main_api_app_v2
[filter:auth]
paste.filter_factory = test_paste_wsgiref:auth_factory
[app:mainversions]
paste.app_factory = test_paste_wsgiref:version_app_factory
[app:main_api_app_v1]
paste.app_factory = test_paste_wsgiref:apiV1_app_factory
[app:main_api_app_v2]
paste.app_factory = test_paste_wsgiref:apiV2_app_factory
看到这个配置文件,可以理解为java 中servlet和filter在web.xml中的配置。
从下往上来解释这个配置文件可能更好理解一点,最下面三段分别定义了三个application,他们的名字分别为mainversions、main_api_app_v1、main_api_app_v2。同时,通过paste.app_factory对应的方法可以返回相应的app。
倒数第四段[filter:auth],定义了一个认证的过滤器,它将过滤掉所有没有授权的请求。
第二段和第三段定义了两个pipeline,表示在访问v1app 和v2app之前需要先进行认证操作。
第一段表示当前存在三个主api接口,其中根目录直接访问mainversions app,v1和v2需要进行pipeline的一系列操作,目前只包括两步(auth,app)
接下来就可以实现相应的application逻辑了:
import cgi
import sys
import os
from paste import deploy
from wsgiref import simple_server
class VersionSelectorApplication(object):
def __init__(self,version = 2):
self._version = version
def __call__(self, environ, start_response):
versions = 'version =' + self._version
status = '200 OK'
header = [('Content-type', 'text/plain')]
start_response(status, header)
return versions
class V1Application(object):
def __call__(self, environ, start_response):
result = 'call api version 1'
status = '200 OK'
header = [('Content-type', 'text/plain')]
start_response(status, header)
return result
class V2Application(object):
def __call__(self, environ, start_response):
result = 'call api version 2'
status = '200 OK'
header = [('Content-type', 'text/plain')]
start_response(status, header)
return result
class _MiniResp(object):
def __init__(self, error_msg, env, headers=[]):
self.body = [error_msg.encode()]
self.headers = list(headers)
self.headers.append(('Content-type', 'text/plain'))
class AuthFilter(object):
def __init__(self, app):
self._app = app
def __call__(self, environ, start_response):
query = cgi.parse_qs(environ['QUERY_STRING'])
username = query.get('username',[''])[0]
password = query.get('password',[''])[0]
if username != 'admin' or password != 'admin':
msg = '401 UnAuthorized'
resp = _MiniResp(msg, environ)
start_response(msg, resp.headers)
return resp.body
else:
status = '200 OK'
return self._app(environ, start_response)
def auth_factory(global_config, **local_config):
def auth_filter(app):
return AuthFilter(app)
return auth_filter
def version_app_factory(global_config, **local_config):
return VersionSelectorApplication()
def apiV1_app_factory(global_config, **local_config):
return V1Application()
def apiV2_app_factory(global_config, **local_config):
return V2Application()
if __name__ == '__main__':
sys.path.append(os.path.abspath('.'))
paste_file = 'api-paste.ini'
app = deploy.loadapp("config:%s" % os.path.abspath(paste_file))
server = simple_server.make_server('127.0.0.1', 8001, app)
server.serve_forever()
application的实现也很简单,先看main方法,主要实现了以下几步:
1.调用loadapp来加载api-paste.ini中定义的app
2.调用make_server来创建服务WSGI的web应用程序,并关联相应的app
3.调用serve_forever表示不断的接收web请求
接着是各个factory的实现,它们都是访问相应的application类。同时,各个application类中都实现了内置的__call__方法,因为这个相应请求时默认调用的方法。其中,_MiniResp类模拟了认证失败时的响应对象。
通过对paste deployment 和wsgiref的学习,希望能对openstack中api模块有更好的了解。
参考文献:
paste deployment官网:http://pythonpaste.org/deploy/
wsgiref官网:https://docs.python.org/2/library/wsgiref.html