Contents
PyLons 请求生命周期
本文对 PyLons 内部调用过程的大致轮廓进行了简单的勾勒.
首先通过命令 paster create --template=PyLons yourproject 创建一个项目. 然后 ==>
" yourproject_dir::command "
# 读取配置文件并启动服务器和应用程序 paster serve development.ini ==>
" yourproject_dir::development.ini "
#PasteScript 的命令会通过配置文件中指定的 egg 的 entry_points 找到相应的对象 [server:main] # 指定 paster serve 使用的 web 服务器 use = egg:Paste#http [app:main] use = egg:yourproject ==>
" yourproject_dir/setup.py "
" yourproject.__init__.py "
1 from yourproject.config.middleware import make_app ==>
" yourproject.config.middleware.py "
1 def make_app(...):
2 ...
3 # 核心 app
4 app = PyLons.wsgiapp.PyLonsApp(config) ==>
5 app = ConfigMiddleware(app, ... )
6 # YOUR MIDDLEWARE
7 # 将 HTTPExceptions 转换成 HTTP responses
8 app = httpexceptions.make_middleware(app, ... )
9 app = ErrorHandler(app, ... )
10 # 处理静态文件
11 static_app = StaticURLParser(...)
12 # 处理 webhelpers 中使用到的 javascript 文件
13 javascripts_app = StaticJavascripts()
14 # 该中间件的作用是: 对列表中的 app 一个一个尝试, 如果 app 产生 404 错误则继续试下一个
15 app = Cascade([static_app, javascripts_app, app])
16 app = ErrorDocuments(app, ... )
17 #
18 app = RegistryManager(app)
19 return app
" PyLons.wsgiapp.py "
" PyLons.wsgiapp.py "
1 class PyLonsBaseWSGIApp(object):
2 def __init__(self ... )
3 ...
4 def __call__(self, environ, start_response):
5 #正常情况下大致执行流程:
6 self.setup_app_env(...)
7 req = PyLons.request._current_obj()
8 # 通过 url dispatcher (routes) 找到相应 controller
9 controller = self.resolve(environ, start_response)
10 # 执行该 controller
11 response = self.dispatch(controller, environ, start_response)
12 status, response_headers, content = response.wsgi_response()
13 start_response(status, response_headers)
14 return content
15 ...
16
17 def dispatch(self, controller, environ, start_response):
18 # 如果是 class, 则实例化之
19 if not hasattr(controller, '__class__') or \
20 getattr(controller, '__class__') == type:
21 controller = controller()
22
23 # 把它当个 wsgi application 调用之
24 return controller(environ, start_response) ==>
" PyLons.controllers.py "
1 class Controller(object):
2 ...
3 def _dispatch_call(self):
4 """将请求分发到具体函数"""
5 action = self._req.environ['PyLons.routes_dict'].get('action')
6 action_method = action.replace('-', '_')
7 func = getattr(self, action_method, None)
8 if isinstance(func, types.MethodType):
9 response = self._inspect_call(func)
10 else:
11 if asbool(CONFIG['global_conf'].get('debug')):
12 raise NotImplementedError('Action %s is not implemented' % action)
13 else:
14 response = PyLons.Response(code=404)
15 return response
16
17 class WSGIController(Controller):
18 ...
19 def __call__(self, environ, start_response):
20 self.start_response = start_response
21 match = environ['PyLons.routes_dict']
22 self._req = PyLons.request._current_obj()
23
24 # 不调用私有方法
25 if match.get('action').startswith('_'):
26 return PyLons.Response(code=404)
27
28 if hasattr(self, '__before__'):
29 self._inspect_call(self.__before__)
30 # 此方法请看上面的 Controller 类
31 response = self._dispatch_call()
32 if hasattr(self, '__after__'):
33 self._inspect_call(self.__after__)
34
35 if hasattr(response, 'wsgi_response'):
36 # 估计返回的是 paste.wsgiwrappers.WSGIResponse
37 status, response_headers, content = response.wsgi_response()
38 start_response(status, response_headers)
39
40 # 如果是在测试, 将 response 放到测试环境中去
41 if environ.get('paste.testing'):
42 environ['paste.testing_variables']['response'] = response
43 response = content
44
45 # 看来是个 WSGI应用程序 的 Response
46 return response
47
48 class RPCController(object):
49 ...
结论
从中我们大致可以看出 PyLons 扩展的潜力:
首先用户代码中通过命令 paster controller controllername 生成的 Controller 都默认继承自 yourproject.lib.base.BaseController ( 后者继承自 PyLons.controllers.WSGIController ), 那么修改 BaseController 可以影响到所有默认生成的 Controller
Controller 中有两个特殊的方法 __before__ 和 __after__, 这个两个方法的行为可以影响到当前 controller 中所有 action
在 yourproject/config/middleware.py 添加其他 wsgi中间件.
在 Controller 中直接调用标准的 WSGI 应用程序 并返回其结果
而且注意到 PyLonsApp 和 WSGIController 都是 WSGI应用程序, 不过里面通过 environ 依赖到一些其他的东西, 比如 routes, 单独拿出来用的意义似乎不太大
- ...