wsgiref+Paste Deployment的使用


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





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章