RoutesMiddleware

RoutesMiddleware
  对于一个应用来说,通常会创建一个Mapper实例,然后调用match()方法;而对于一个WSGI框架来说,会使用类似下面的代码来使用RoutesMiddleware:

from routes.middleware import RoutesMiddleware
wsgi_app = RoutesMiddleware(app, map)

  middleware会匹配请求的URl,并设置以下WSGI变量:

environ['wsgiorg.routing_args'] = ((url, match))
environ['routes.route'] = route
environ['routes.url'] = url

  其中,url是URLGenerator的实例,match是一个包含匹配信息的字典,route是匹配的路由。
  下面,使用middleware创建一个WSGI的Web框架,此框架提供了路由建立和路由匹配的功能,应用层只需专注于业务即可。
  整个WSGI的Web框架在文件wsgi.py中实现。代码如下:

import json
import routes
import routes.middleware
import webob
import webob.dec
import webob.exc

class APIMapper(routes.Mapper):
    pass

class Router(object):
    def __init__(self, mapper):
        mapper.redirect("", "/")
        self.map = mapper
        self._router = routes.middleware.RoutesMiddleware(self._dispatch,
                                                         self.map)

    @classmethod
    def factory(cls, global_conf={}, **local_conf):
        return cls(APIMapper())

    @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

class Request(webob.Request):
    pass

class Resource(object):
    def __init__(self, controller, deserializer=None, serializer=None):
        self.controller = controller

    @webob.dec.wsgify(RequestClass=Request)
    def __call__(self, request):
        action_args = self.get_action_args(request.environ)
        action = action_args.pop('action', None)

        action_result = self.dispatch(self.controller, action,
                                     request, **action_args)
        try:
            response = webob.Response(request=request)
            response.content_type = 'application/json'
            response.body = json.dumps(action_result)
            return response

        except webob.exc.HTTPException as e:
            return e
        except Exception as e:
            print('e: %r' % e)
            return action_result

    def dispatch(self, obj, action, *args, **kwargs):
        try:
            method = getattr(obj, action)
        except AttributeError:
            return {}

        return method(*args, **kwargs)

    def get_action_args(self, request_environment):
        try:
            args = request_environment['wsgiorg.routing_args'][1].copy()
        except Exception:
            return {}

        try:
            del args['controller']
        except KeyError:
            pass

        try:
            del args['format']
        except KeyError:
            pass

        return args

  业务层的代码位于文件router.py中。主要是调用了框架的接口,实现了URL映射的建立,以及业务功能的具体实现。代码如下:

import wsgi

class VolumeController(object):
    def __init__(self):
        pass

    def index(self, req):
        return [
            {'id': 1, 'name': 'volume-a'},
            {'id': 2, 'name': 'volume-b'}
                ]

class APIRouter(wsgi.Router):
     def __init__(self,mapper):
         mapper.resource('volume', 'volumes',
                         controller=wsgi.Resource(VolumeController()))
         super(APIRouter, self).__init__(mapper)

  程序的入口位于文件main.py中,主要是通过wsgiref提供的make_server()函数,启动一个WSGI服务。代码如下:

from wsgiref.simple_server import make_server
import router

wsgi_app = router.APIRouter.factory()
server = make_server('localhost',8080,wsgi_app)
server.serve_forever()

  执行python main.py后,在终端使用curl命令访问此WSGI服务,可以发现WSGI服务工作正常。

[root@localhost ~]# curl http://localhost:8080/volumes
[{"id": 1, "name": "volume-a"}, {"id": 2, "name": "volume-b"}][root@localhost ~]#
[root@localhost ~]#

参考资料:
  Porting Routes to a WSGI Web Framework