大部分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