正在看Nova API的代码, 仿照其中的REST 实现逻辑写了一个测试程序.
Nova API 当然支持了更多的功能, 例如XML/JSON序列化, 身份验证等等. 这个测试程序只实现其中的REST调用逻辑
为了容易读懂, 我去掉了其中的类继承逻辑
MyRouter: 负责把HTTP请求, 根据匹配规则, 分发到不同的Application中
1) [ Python Deploy ] 收到HTTP请求后, 先调用 MyRouter的 __call__方法进行处理
2) [ MyRouter ] 根据内部的匹配定义, 通过RouteMiddleware 分发到对应的 MyApplication
MyApplication: 收到HTTP 请求后, 解析其中的参数信息, 调用后台的MyController业务类的对应方法
MyController: 最终的业务类, 接受参数, 执行业务逻辑的操作.
from __future__ import print_function from routes import Mapper import webob.dec import webob.exc import routes.middleware import testtools class MyController(object): def getlist(self, mykey): print("step 4: MyController's getlist(self, mykey) is invoked") return "getlist(), mykey=" + mykey class MyApplication(object): """Test application to call from router.""" def __init__(self, controller): self._controller = controller def __call__(self, environ, start_response): print("step 3: MyApplication is invoked") action_args = environ['wsgiorg.routing_args'][1].copy() try: del action_args['controller'] except KeyError: pass try: del action_args['format'] except KeyError: pass action = action_args.pop('action', None) controller_method = getattr(self._controller, action) result = controller_method(**action_args) start_response('200 OK', [('Content-Type', 'text/plain')]) return [result] class MyRouter(object): """Test router.""" def __init__(self): route_name = "dummy_route" route_path = "/dummies" my_application = MyApplication(MyController()) self.mapper = Mapper() self.mapper.connect(route_name, route_path, controller=my_application, action="getlist", mykey="myvalue", conditions={"method": ['GET']}) self._router = routes.middleware.RoutesMiddleware(self._dispatch, self.mapper) @webob.dec.wsgify(RequestClass=webob.Request) def __call__(self, req): """Route the incoming request to a controller based on self.map. If no match, return a 404. """ print("step 1: MyRouter is invoked") return self._router @staticmethod @webob.dec.wsgify(RequestClass=webob.Request) def _dispatch(req): """Dispatch the request to the appropriate controller. Called by self._router after matching the incoming request to a route and putting the information into req.environ. Either returns 404 or the routed WSGI app's response. """ print("step 2: RoutesMiddleware is invoked, calling our _dispatch back") match_dict = req.environ['wsgiorg.routing_args'][1] if not match_dict: return webob.exc.HTTPNotFound() app = match_dict['controller'] return app class RoutingTestCase(testtools.TestCase): def test_router(self): router = MyRouter() result = webob.Request.blank('/dummies').get_response(router) self.assertEqual(result.body, "getlist(), mykey=myvalue")
从输出结果, 可以看出调用顺序是: MyRouter --> routes.middleware.RoutesMiddleware --> MyApplication --> MyController:
[Test Case] test_router step 1: MyRouter is invoked step 2: RoutesMiddleware is invoked, calling our _dispatch back step 3: MyApplication is invoked step 4: MyController's getlist(self, mykey) is invoked