设为首页 收藏本站
查看: 1401|回复: 0

[经验分享] Python paste模块和routes模块

[复制链接]
累计签到:2 天
连续签到:1 天
发表于 2015-4-26 10:55:52 | 显示全部楼层 |阅读模式
  在阅读openstack源码时,发现其各个组件基本都是使用paste模块和routes模块来构建server以及处理路由信息的,为了熟悉这些模块决定使用这些模块写一个简单的server,此server的构架和glance组件的设计保持一致。
  首先使用paste创建一个app,然后wsgiref.simple_server启动他,至于app的功能通过配置文件来决定



1 config = "python_paste.ini"
2 appname = "common"
3 wsgi_app = loadapp("config:%s" % os.path.abspath(config), appname)
4  server = make_server('localhost',80,wsgi_app)
5 server.serve_forever()
  python_paste.ini



[composite:common]
use = egg:Paste#urlmap
/:showversion
/log:showversion_log
/v1:apiv1app
[pipeline:showversion_log]
pipeline = filter_log showversion
[filter:filter_log ]
#filter2 deal with time,read args belowmanage
paste.filter_factory = manage:LogFilter.factory
[app:apiv1app]
paste.app_factory = v1.router:MyRouterApp.factory
[app:showversion]
version = 1.0.0
paste.app_factory = manage:ShowVersion.factory
/:showversion:打印版本号
/log:showversion_log:打印版本号的同时做相应的日志记录
/v1:apiv1app:模仿glance中的多版本api,将所有v1的请求转发apiv1app处理。


1 class ShowVersion(object):
2       '''
3       app
4       '''
5       def __init__(self,version):
6           self.version = version
7       def __call__(self,environ,start_response):
8           res = Response()
9           res.status = '200 OK'
10           res.content_type = "text/plain"
11           content = []
12           content.append("%s\n" % self.version)
13           res.body = '\n'.join(content)
14           return res(environ,start_response)
15       @classmethod
16       def factory(cls,global_conf,**kwargs):
17           print 'factory'
18           print "kwargs:",kwargs
19           return ShowVersion(kwargs['version'])


1 class LogFilter(object):
2       '''
3       Log
4       '''
5       def __init__(self,app):
6           self.app = app
7       def __call__(self,environ,start_response):
8           print 'you can write log.‘
9           return self.app(environ,start_response)
10       @classmethod
11       def factory(cls,global_conf,**kwargs):
12           return LogFilter
  到此uri为/和/log时,均可得到服务器的正常响应。但是/v1还没有实现。不得不佩服python的强大,这么几行就实现了一个web服务器的基本功能,下面在介绍routes,配合routes就能够实现更多的更优雅的uri路由机制。
  注:此处对paste的配置文件介绍不多,大家去paste官网查看即可,写的很详细了,这个也不是很难,没必要去一一介绍。
  新建一个python模块v1,在v1里面新建两个文件router.py和wsgi.py,wsgi.py是一个通用文件,和业务无关,我们只需要关注router.py的实现即可,使用起来非常简单方便。
  router.py:



1 import wsgi
2
3 class ControllerTest(object):
4     def __init__(self):
5         print "ControllerTest!!!!"
6     def test(self,req):
7           print "req",req
8           return {
9             'name': "test",
10             'properties': "test"
11         }
12
13 class MyRouterApp(wsgi.Router):
14       '''
15       app
16       '''
17       def __init__(self,mapper):
18           controller = ControllerTest()
19           mapper.connect('/test',
20                        controller=wsgi.Resource(controller),
21                        action='test',
22                        conditions={'method': ['GET']})
23           super(MyRouterApp, self).__init__(mapper)
  wsgi.py:



  1 import datetime
  2 import json
  3 import routes
  4 import routes.middleware
  5 import webob
  6 import webob.dec
  7 import webob.exc
  8
  9 class APIMapper(routes.Mapper):
10     """
11     Handle route matching when url is '' because routes.Mapper returns
12     an error in this case.
13     """
14
15     def routematch(self, url=None, environ=None):
16         if url is "":
17             result = self._match("", environ)
18             return result[0], result[1]
19         return routes.Mapper.routematch(self, url, environ)
20
21 class Router(object):
22     def __init__(self, mapper):
23         mapper.redirect("", "/")
24         self.map = mapper
25         self._router = routes.middleware.RoutesMiddleware(self._dispatch,
26                                                           self.map)
27
28     @classmethod
29     def factory(cls, global_conf, **local_conf):
30         return cls(APIMapper())
31
32     @webob.dec.wsgify
33     def __call__(self, req):
34         """
35         Route the incoming request to a controller based on self.map.
36         If no match, return a 404.
37         """
38         return self._router
39
40     @staticmethod
41     @webob.dec.wsgify
42     def _dispatch(req):
43         """
44         Called by self._router after matching the incoming request to a route
45         and putting the information into req.environ.  Either returns 404
46         or the routed WSGI app's response.
47         """
48         match = req.environ['wsgiorg.routing_args'][1]
49         if not match:
50             return webob.exc.HTTPNotFound()
51         app = match['controller']
52         return app
53
54 class Request(webob.Request):
55     """Add some Openstack API-specific logic to the base webob.Request."""
56
57     def best_match_content_type(self):
58         """Determine the requested response content-type."""
59         supported = ('application/json',)
60         bm = self.accept.best_match(supported)
61         return bm or 'application/json'
62
63     def get_content_type(self, allowed_content_types):
64         """Determine content type of the request body."""
65         if "Content-Type" not in self.headers:
66             return
67
68         content_type = self.content_type
69
70         if content_type not in allowed_content_types:
71             return
72         else:
73             return content_type
74
75 class JSONRequestDeserializer(object):
76     def has_body(self, request):
77         """
78         Returns whether a Webob.Request object will possess an entity body.
79
80         :param request:  Webob.Request object
81         """
82         if 'transfer-encoding' in request.headers:
83             return True
84         elif request.content_length > 0:
85             return True
86
87         return False
88
89     def _sanitizer(self, obj):
90         """Sanitizer method that will be passed to json.loads."""
91         return obj
92
93     def from_json(self, datastring):
94         try:
95             return json.loads(datastring, object_hook=self._sanitizer)
96         except ValueError:
97             msg = _('Malformed JSON in request body.')
98             raise webob.exc.HTTPBadRequest(explanation=msg)
99
100     def default(self, request):
101         if self.has_body(request):
102             return {'body': self.from_json(request.body)}
103         else:
104             return {}
105
106 class JSONResponseSerializer(object):
107
108     def _sanitizer(self, obj):
109         """Sanitizer method that will be passed to json.dumps."""
110         if isinstance(obj, datetime.datetime):
111             return obj.isoformat()
112         if hasattr(obj, "to_dict"):
113             return obj.to_dict()
114         return obj
115
116     def to_json(self, data):
117         return json.dumps(data, default=self._sanitizer)
118
119     def default(self, response, result):
120         response.content_type = 'application/json'
121         response.body = self.to_json(result)
122
123 class Resource(object):
124     def __init__(self, controller, deserializer=None, serializer=None):
125         self.controller = controller
126         self.serializer = serializer or JSONResponseSerializer()
127         self.deserializer = deserializer or JSONRequestDeserializer()
128
129     @webob.dec.wsgify(RequestClass=Request)
130     def __call__(self, request):
131         """WSGI method that controls (de)serialization and method dispatch."""
132         action_args = self.get_action_args(request.environ)
133         action = action_args.pop('action', None)
134
135         deserialized_request = self.dispatch(self.deserializer,
136                                              action, request)
137         action_args.update(deserialized_request)
138
139         action_result = self.dispatch(self.controller, action,
140                                       request, **action_args)
141         try:
142             response = webob.Response(request=request)
143             self.dispatch(self.serializer, action, response, action_result)
144             return response
145
146         except webob.exc.HTTPException as e:
147             return e
148         # return unserializable result (typically a webob exc)
149         except Exception:
150             return action_result
151
152     def dispatch(self, obj, action, *args, **kwargs):
153         """Find action-specific method on self and call it."""
154         try:
155             method = getattr(obj, action)
156         except AttributeError:
157             method = getattr(obj, 'default')
158
159         return method(*args, **kwargs)
160
161     def get_action_args(self, request_environment):
162         """Parse dictionary created by routes library."""
163         try:
164             args = request_environment['wsgiorg.routing_args'][1].copy()
165         except Exception:
166             return {}
167
168         try:
169             del args['controller']
170         except KeyError:
171             pass
172
173         try:
174             del args['format']
175         except KeyError:
176             pass
177
178         return args
我们使用 mapper.connect 接口创建路由信息:
/test :uri
controller:控制器对象
action:控制器对象中的方法,即在请求uri时最终执行的方法
contitions:请求类型


1           mapper.connect('/test',
2                        controller=wsgi.Resource(controller),
3                        action='test',
4                        conditions={'method': ['GET']})
  在这个例子中,当我们执行/v1/test 即可得到如下回复:
  {
            'name': "test",
            'properties': "test"
        }
  
  本文介绍的server框架和glance一模一样,和keystone也是大同小异。
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-60824-1-1.html 上篇帖子: 线程池原理及python实现 下篇帖子: 使用Python控制IE访问网站
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表