openstack 中的WSGI
来源:互联网 发布:2017网络博客娱乐网站 编辑:程序博客网 时间:2024/05/08 04:38
1 WSGI 程序起步
本文中程序的放置路径及运行方式在 Window + Apache + WSGI 配置指明。
第一WSGI程序
- def spch_wsgi(environ, start_response):
- status = '200 OK'
- response_headers = [('Content-Type', 'text/plain')]
- start_response(status, response_headers)
- return ['Hello World!']
- application = spch_wsgi
WSGI server检索application函数, 并传递两个参数environ, start_response。
environ 为一个字典,包含环境变量。
start_response 为一个函数, 用于返回状态信息。
一个WSGI程序要完成两件事:
其一:返回HTTP header。本例中, 状态‘200 OK‘, 表明一切正常。
其二:返回一个iterable containing, 本例中是一个list。
输出environ信息
- def application(environ, start_response):
- response_body = ""
- for k in environ:
- tmp = "%s = %s \n" % (k, environ[k])
- response_body += tmp
- status = '200 OK'
- response_headers = [('Content-Type', 'text/plain')]
- start_response(status, response_headers)
- return [response_body]
- wsgi.multiprocess = False
- SERVER_PROTOCOL = HTTP/1.1
- SERVER_SOFTWARE = Apache/2.2.22 (Win32) mod_wsgi/3.3 Python/2.7.4
- SCRIPT_NAME = /wsgi
- mod_wsgi.handler_script =
- SERVER_SIGNATURE =
- REQUEST_METHOD = GET
- PATH_INFO =
- PATHEXT = .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
- QUERY_STRING =
- HTTP_USER_AGENT = Mozilla/5.0 (Windows NT 6.2; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0
- HTTP_CONNECTION = keep-alive
- SERVER_NAME = localhost
- REMOTE_ADDR = 127.0.0.1
- mod_wsgi.request_handler = wsgi-script
- wsgi.url_scheme = http
- mod_wsgi.callable_object = application
- SERVER_PORT = 80
- mod_wsgi.version = (3, 3)
- mod_wsgi.input_chunked = 0
- SERVER_ADDR = 127.0.0.1
- DOCUMENT_ROOT = D:/Program Files (x86)/Apache Software Foundation/Apache2.2/htdocs
- mod_wsgi.process_group =
- COMSPEC = C:\Windows\system32\cmd.exe
- SCRIPT_FILENAME = C:/wsgi_app/wsgi_handler.py
- SERVER_ADMIN = admin@localhost.com
- wsgi.input = <mod_wsgi.Input object at 0x01379DE0>
- HTTP_HOST = localhost
- wsgi.multithread = True
- SystemRoot = C:\Windows
- REQUEST_URI = /wsgi
- HTTP_ACCEPT = text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
- WINDIR = C:\Windows
- wsgi.version = (1, 1)
- GATEWAY_INTERFACE = CGI/1.1
- wsgi.run_once = False
- wsgi.errors = <mod_wsgi.Log object at 0x01379D40>
- REMOTE_PORT = 64214
- HTTP_ACCEPT_LANGUAGE = zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
- mod_wsgi.application_group = 192.168.209.1|/wsgi
- mod_wsgi.script_reloading = 1
- wsgi.file_wrapper = <built-in method file_wrapper of mod_wsgi.Adapter object at 0x012E1770>
- HTTP_ACCEPT_ENCODING = gzip, deflate
上述代码也可以通过类来实现,类中要重载__call__,这样的好处是可以从其它类继承,复用代码。
- class MyApp:
- def __call__(self, environ, start_response):
- response_body = ['Hello World!']
- status = '200 OK'
- response_headers = [('Content-Type', 'text/plain')]
- start_response(status, response_headers)
- return response_body
- application = MyApp()
2 WSGI-- Middleware
假定存在一个superSession模块,用于追踪用户访问行为。
- import superSession
- session = superSession.session()
- print "Content-type: text/plain\n\n"
- if session.has_key('visited'):
- print "You have already visited!"
- else:
- session['visited'] = 1
- print "This is your first visit."
上述代码创建了一个Session对象,追踪用户访问行为。将上述思想用于WSGI程序中。
- def application(environ, start_response):
- import superSession
- session = superSession.session()
- if session.has_key('visited'):
- text = "You have already visited!"
- else:
- session['visited'] = 1
- text = "This is your first visit."
- start_response('200 OK', [('Content-type','text/plain')])
- return [text]
可以将上述代码进行重构。
- def exampleApplication(environ, start_response):
- if environ['superSession'].has_key('visited'):
- text = "You have already visited!"
- else:
- environ['superSession']['visited'] = 1
- text = "This is your first visit."
- start_response('200 OK', [('Content-type','text/plain')])
- return [text]
- def session(application):
- def app(environ, start_response):
- if "superSession" not in environ:
- import superSession
- environ["superSession"] = superSession.session()
- return application(environ, start_response)
- return app
- application = session(exampleApplication)
将session代码抽离放于session函数中,该函数专门用于判断用户访问行为。session函数将判断结果至于环境变量environ字典中。
exampleApplication通过environ字典获得用户访问行为。
我们称session函数为middleware,它处于server与application之间,对server传来的请求做相应的处理;它对于Server和application是透明的。
middleware的好处在于,通过middleware(本例中session函数)可以很简单的给WSGI程序添加新功能。
我们也可见将middleware包装成类,这样,我们可以通过继承,复用现有的中间件。类中要重载__call__。
- class Session:
- def __init__(self, application):
- self.application = application
- def __call__(self, environ, start_response):
- if "superSession" not in environ:
- import superSession
- environ["superSession"] = superSession.session() # Options would obviously need specifying
- return self.application(environ,start_response)
- application = Session(exampleApplication)
附录: 代码语法解释
- def session(application):
- def app(environ, start_response):
- if "superSession" not in environ:
- import superSession
- environ["superSession"] = superSession.session()
- return application(environ, start_response)
- return app
- application = session(exampleApplication)
将exampleApplication传入session函数,session函数中定义了一个新的函数app,session将app返回赋给application。
实际上相当于application = app。app函数中进行相应处理(superSession),将处理好的environ在传递给exampleApplication。
3 webob request response
Request
Webob的Request对象,提供对WSGI environ环境变量的包装,通过webob可以很容易的读写environ字典。
environ字典内容如下:
- TMP = C:\Users\spch2008\AppData\Local\Temp
- PYTHONIOENCODING = GBK
- COMPUTERNAME = SPCH2008
- wsgi.multiprocess = False
- PROCESSOR_LEVEL = 16
- USERDOMAIN = SPCH2008
- VS100COMNTOOLS = D:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\Tools\
- HTTP_ACCEPT_LANGUAGE = zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
- SERVER_PROTOCOL = HTTP/1.1
- SERVER_SOFTWARE = WSGIServer/0.1 Python/2.7.4
- PSMODULEPATH = C:\Windows\system32\WindowsPowerShell\v1.0\Modules\
- SCRIPT_NAME =
- COMMONPROGRAMFILES = C:\Program Files (x86)\Common Files
- PROCESSOR_IDENTIFIER = AMD64 Family 16 Model 5 Stepping 3, AuthenticAMD
- REQUEST_METHOD = GET
- PROGRAMFILES = C:\Program Files (x86)
- PROCESSOR_REVISION = 0503
- PATH = D:/Program Files (x86)/java/jre7/bin/client;D:/Program Files (x86)/java/jre7/bin;D:/Program Files (x86)/java/jre7/lib/i386;C:\python32\;C:\python32\Lib\site-packages\;C:\python32\Scripts\;C:\Program Files (x86)\Common Files\NetSarang;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;D:\Program Files (x86)\java\jre7\bin;D:\Program Files (x86)\Rational\common;D:\Program Files (x86)\eclipse;
- QUERY_STRING =
- SYSTEMROOT = C:\Windows
- PROGRAMFILES(X86) = C:\Program Files (x86)
- PT5HOME = d:\Program Files (x86)\Cisco Packet Tracer 5.3.3
- CONTENT_LENGTH =
- HTTP_USER_AGENT = Mozilla/5.0 (Windows NT 6.2; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0
- HTTP_CONNECTION = keep-alive
- TEMP = C:\Users\spch2008\AppData\Local\Temp
- REMOTE_ADDR = 127.0.0.1
- COMMONPROGRAMFILES(X86) = C:\Program Files (x86)\Common Files
- PROCESSOR_ARCHITECTURE = x86
- wsgi.url_scheme = http
- ALLUSERSPROFILE = C:\ProgramData
- PYDEV_CONSOLE_ENCODING = GBK
- SERVER_PORT = 8080
- LOCALAPPDATA = C:\Users\spch2008\AppData\Local
- HOMEPATH = \Users\spch2008
- USERDOMAIN_ROAMINGPROFILE = SPCH2008
- PROGRAMW6432 = C:\Program Files
- USERNAME = spch2008
- HTTP_ACCEPT = text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
- LOGONSERVER = \\MicrosoftAccount
- PROMPT = $P$G
- COMSPEC = C:\Windows\system32\cmd.exe
- PROGRAMDATA = C:\ProgramData
- PYTHONPATH = D:\Program Files (x86)\eclipse\plugins\org.python.pydev_2.7.3.2013031601\pysrc\pydev_sitecustomize;E:\GitHub\OpenStack\WSGI;C:\python32\DLLs;C:\python32\lib;C:\python32\lib\plat-win;C:\python32\lib\lib-tk;C:\python32;C:\python32\lib\site-packages
- PATH_INFO = /
- wsgi.multithread = True
- wsgi.input = <socket._fileobject object at 0x0285C030>
- wsgi.errors = <open file '<stderr>', mode 'w' at 0x01DA60D0>
- HTTP_HOST = localhost:8080
- SESSIONNAME = Console
- PATHEXT = .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
- ASL.LOG = Destination=file
- FP_NO_HOST_CHECK = NO
- WINDIR = C:\Windows
- wsgi.file_wrapper = wsgiref.util.FileWrapper
- HTTP_ACCEPT_ENCODING = gzip, deflate
- wsgi.version = (1, 0)
- APPDATA = C:\Users\spch2008\AppData\Roaming
- HOMEDRIVE = C:
- SERVER_NAME = spch2008
- wsgi.run_once = False
- REMOTE_HOST = spch2008
- SYSTEMDRIVE = C:
- GATEWAY_INTERFACE = CGI/1.1
- PYDEV_COMPLETER_PYTHONPATH = D:\Program Files (x86)\eclipse\plugins\org.python.pydev_2.7.3.2013031601\pysrc
- NUMBER_OF_PROCESSORS = 4
- DJANGO_SETTINGS_MODULE = WSGI.settings
- CONTENT_TYPE = text/plain
- PROCESSOR_ARCHITEW6432 = AMD64
- COMMONPROGRAMW6432 = C:\Program Files\Common Files
- OS = Windows_NT
- PUBLIC = C:\Users\Public
- USERPROFILE = C:\Users\spch2008
- req = Request(environ)
通过Request操作上述环境变量,所得结果如下:
req.method 'Get'
req.path_info '/'
req.content_type 'text/plain'
req.remote_user 'None'
req.host 'localhost:8080'
即通过req,可以很方便的读取environ环境变量,更多操作请看:http://docs.webob.org/en/latest/modules/webob.html
Response
Response包含了所有响应WSGI Server需要的变量。
- res = Response()
- res.status = 200
- res.headerlist = [('Content-type', 'text/html')]
- res.body = 'Hello World!'
使用webob改写之前的Hello World程序。j将上述代码粘贴到eclipse中,运行。
- from wsgiref.simple_server import make_server
- from webob import Request, Response
- class MyApp:
- def __call__(self, environ, start_response):
- req = Request(environ)
- res = Response()
- res.status = 200
- res.headerlist = [('Content-Type', 'text/plain')]
- res.body = "Hello World!"
- return res(environ, start_response)
- application = MyApp()
- httpd = make_server('localhost', 8080, application)
- httpd.serve_forever()
wsgify装饰器将一个普通函数转变成WSGI应用程序。
class webob.dec.wsgify(func=None, RequestClass=None, args=(), kwargs=None, middleware_wraps=None)
小示例
- from wsgiref.simple_server import make_server
- from webob import Request, Response
- from webob.dec import *
- @wsgify
- def test(req):
- res = Response()
- res.status = 200
- res.body = "spch"
- return res
- class MyApp:
- def __call__(self, environ, start_response):
- req = Request(environ)
- return test(environ, start_response)
- application = MyApp()
- httpd = make_server('localhost', 8081, application)
- httpd.serve_forever()
而且,我们可以定制装饰器
- from wsgiref.simple_server import make_server
- from webob import Request, Response
- from webob.dec import *
- from webob.exc import *
- class MyRequest(Request):
- @property
- def is_local(self):
- return self.remote_addr == '127.0.0.1'
- @wsgify(RequestClass=MyRequest)
- def myfunc(req):
- if req.is_local:
- return Response('hi!')
- else:
- raise HTTPForbidden
- class MyApp:
- def __call__(self, environ, start_response):
- req = Request(environ)
- return myfunc(environ, start_response)
- application = MyApp()
- httpd = make_server('localhost', 8081, application)
- httpd.serve_forever()
如何是本机访问,则输出’hi‘,否则不允许
- from routes import Mapper
- map = Mapper()
- map.connect('spch', '/blog', controller='main', action='index')
- result = map.match('/blog')
- print result
- {'action': u'index', 'controller': u'main'}
1.2 行创建一个mapper
3. 行注册一条路由, 路由名称为'spch', 路径为'/blog', controller为main,
action为index
可以这样认为,匹配到此条路由的请求交由controller处理,请求预调用的
函数为index
5. 创建好路由条目后,即可以进行匹配,调用match方法,匹配路径'blog'
6. 输出匹配结果
- map.connect(None, "/error/{action}/{id}", controller="error")
- result = map.match('/error/index/2')
- print result
- {'action': u'index', 'controller': u'error', 'id': u'2'}
1.注册了一条无名路由,并且action从匹配路由中获得
同样,我们可以省掉None
map.connect("/error/{action}/{id}", controller="error")
上述语句同样注册了一条无名路由。
Conditions
Conditions用于限制进行路由匹配,比如method
- m.connect("/user/list", controller="user", action="list", conditions=dict(method=["GET", "HEAD"]))
Requirements
有时只想匹配数字,或者匹配可选的几个条目
- map.connect(R"/blog/{id:\d+}")
- map.connect(R"/download/{platform:windows|mac}/{filename}")
\d表示匹配1位数字,\d+表示匹配多位
windows|mac 表示只匹配windows或者mac
可以将上述写成
- map.connect("/blog/{id}", requirements={"id": R"\d+"}
- map.connect("/download/{platform}/{filename}",
- requirements={"platform": R"windows|mac"})
Format extensions
通过{.format}来指定匹配格式
- map.connect('/entries/{id}{.format}')
- print map.match('/entries/2')
- {'id': u'2', 'format': None}
- print map.match('/entries/2.mp3')
- {'id': u'2', 'format': u'mp3'}
- map.connect('/entries/{id:\d+}{.format:mp3}')
- print map.match('/entries/2.mp3')
- {'id': u'2', 'format': u'mp3'}
- print map.match('/entries/2')
- {'id': u'2', 'format': None}
- print map.match('/entries/2.mp4')
- None
注意:{id:\d+}, 如果没有\d+, print map.match('/entries/2.mp4')将输出 {'id': u'2.mp4', 'format': None}是可以成功的。
有了\d+后,由于没有匹配format,同时\d+要求只匹配数字,所有2.mp4匹配失败
当路由条目过多时,需要一条一条注册,过于麻烦,此时可以通过resource route简化
- map.connect("messages", "/messages",
- controller="messages", action="create",
- conditions=dict(method=["POST"]))
- map.connect("messages", "/messages",
- controller="messages", action="index",
- conditions=dict(method=["GET"]))
- map.connect("formatted_messages", "/messages.{format}",
- controller="messages", action="index",
- conditions=dict(method=["GET"]))
- map.connect("new_message", "/messages/new",
- controller="messages", action="new",
- conditions=dict(method=["GET"]))
- map.connect("formatted_new_message", "/messages/new.{format}",
- controller="messages", action="new",
- conditions=dict(method=["GET"]))
- map.connect("/messages/{id}",
- controller="messages", action="update",
- conditions=dict(method=["PUT"]))
- map.connect("/messages/{id}",
- controller="messages", action="delete",
- conditions=dict(method=["DELETE"]))
- map.connect("edit_message", "/messages/{id}/edit",
- controller="messages", action="edit",
- conditions=dict(method=["GET"]))
- map.connect("formatted_edit_message", "/messages/{id}.{format}/edit",
- controller="messages", action="edit",
- conditions=dict(method=["GET"]))
- map.connect("message", "/messages/{id}",
- controller="messages", action="show",
- conditions=dict(method=["GET"]))
- map.connect("formatted_message", "/messages/{id}.{format}",
- controller="messages", action="show",
- conditions=dict(method=["GET"]))
上述路由条目可以使用这一条语句代替。
- map.resource("message", "messages")
函数原型:resource(member_name, collection_name, **kwargs)
- GET /messages => messages.index() => url("messages")
- POST /messages => messages.create() => url("messages")
- GET /messages/new => messages.new() => url("new_message")
- PUT /messages/1 => messages.update(id) => url("message", id=1)
- DELETE /messages/1 => messages.delete(id) => url("message", id=1)
- GET /messages/1 => messages.show(id) => url("message", id=1)
- GET /messages/1/edit => messages.edit(id) => url("edit_message", id=1)
这里有必要说一下member 路由与 collection路由。
上述的路由模型
- GET /messages => messages.index()
- POST /messages => messages.create()
- GET /messages/new => messages.new()
- PUT /messages/1 => messages.update(id)
- DELETE /messages/1 => messages.delete(id)
- GET /messages/1 => messages.show(id)
- GET /messages/1/edit => messages.edit(id)
1. 有的路由有id, 指向一个具体的对象
2. 有的路由没有id, 指向全体对象
3. 有的路由(index/create, show/update/delete)有相同的URL,但是HTTP method不同
4. 有的路由(show/edit)HTTP method和前缀相同,仅后缀不同
一个member路由指定具体实例,也就是说它们有id。而一个collection路由,
没有指定的实例,即没有给定id
综上:member路由操作一个单独的实例,而collection操作全体实例。
另一个函数collection也可以完成上述功能。
函数原型:collection(collection_name, resource_name, path_prefix=None, member_prefix='/{id}', controller=None, collection_actions=['index', 'create', 'new'],member_actions=['show', 'update', 'delete', 'edit'], member_options=None, **kwargs)
用法:
map.collection('entries', 'entry')
RoutesMiddleware将请求应声到相应WSGI程序,它将路由匹配结果存到environ环境变量中去。
- from routes.middleware import RoutesMiddleware
- app = RoutesMiddleware(wsgi_app, map) # ``map`` is a routes.Mapper.
map调用match匹配URL,并设置WSGI环境变量
- environ['wsgiorg.routing_args'] = ((url, match))
- environ['routes.route'] = route
- environ['routes.url'] = url
route为匹配到的路由,url为一个URLGenerator对象,match为匹配所得条目。
app为一个RoutesMiddleware对象,内部重载__call__(def __call__(self, environ, start_response))仍为一个wsgi应用。
wsgi_app为一个wsgi程序,RoutesMiddleware将环境变量(environ)设置好后,调用wsgi_app进行后续处理。
下面是一个实际的输出:
- wsgiorg.routing_args = (<routes.util.URLGenerator object at 0x0287AFB0>,
- {'action': u'index', 'controller': <__main__.Resourse instance at 0x02876E40>})
- routes.route = <routes.route.Route object at 0x02871F10>
- routes.url = <routes.util.URLGenerator object at 0x0287AFB0>
1.下载库文件
webob库:http://download.csdn.net/detail/spch2008/5497755
routes库:http://download.csdn.net/detail/spch2008/5497757
repoze库:http://download.csdn.net/detail/spch2008/5499231
2. 组织代码
3. 代码
- '''''
- Created on 2013-6-1
- @author: spch2008
- '''
- from wsgiref.simple_server import make_server
- import routes.middleware
- import webob.dec
- import webob.exc
- class Controller:
- @webob.dec.wsgify
- def __call__(self, req):
- return webob.Response("Hello World!")
- class Router(object):
- def __init__(self):
- self._mapper = routes.Mapper()
- self._mapper.connect('/spch',
- controller=Controller(),
- action='index',
- conditions={'method': ['GET']})
- self._router = routes.middleware.RoutesMiddleware(self._dispatch, self._mapper)
- @webob.dec.wsgify
- def __call__(self, req):
- return self._router
- @staticmethod
- @webob.dec.wsgify
- def _dispatch(req):
- match = req.environ['wsgiorg.routing_args'][1]
- if not match:
- return webob.exc.HTTPNotFound()
- app = match['controller']
- return app
- app = Router()
- httpd = make_server('localhost', 8282, app)
- httpd.serve_forever()
22行:创建一个mapper
23行:#注册一个路由
28行:创建一个RoutesMiddleware对象,匹配路由,修改环境变量后,调用self._dispatch
4. 运行结果
- openstack 中的WSGI
- 0.2 OpenStack-python-wsgi服务
- openstack学习笔记----WSGI(一)
- openstack wsgi发布新路由
- Python中的WSGI
- Django 中的 WSGI
- openstack nova 基础知识——wsgi
- 【OpenStack】WSGI and Webob+Paste示例
- openstack nova 基础知识——wsgi
- Openstack Keystone 认证流程(三)-WSGI
- openstack从零开始(1)——基础知识WSGI
- eventlet.wsgi 中的 start_response定义
- WSGI
- WSGI
- WSGI
- WSGI
- WSGI
- WSGI
- 加上web.xml之后启动tomcat总是失败
- /test.jsp (line: 4, column: 16) equal symbol expected
- 读写分离实践:mysql-proxy
- 微软官方自带例子:vc和c# VB等
- uva 1401 && Live Archive 3942 Remember the Word
- openstack 中的WSGI
- webbench
- hdu 5104 Primes Problem
- 单独css文件中设置中文字体
- 堆栈数据代码区
- C# winform开发datagridview绑定List泛型问题
- Intent 的不同用法
- Java中调用Oracle函数
- uva 1328 && Live Archive 3026 Period