来杯咖啡看Pecan
Pecan的介绍及安装
文章内容来自官方文档:http://pecan.readthedocs.io/en/latest/quick_start.html
Pecan的介绍:
Pecan是一个路由对象分发的oython web框架。本质上可以将url通过分割为每一部分,然后对每一部分查找对应处理该URL部分的处理类,处理后,继续交给后面部分的URL处理,直到所有URL部分都被处理后,调用最后分割的URL对应的处理函数处理。
本文以Xshall为主在其进行操作
Pecan的安装
创建项目
项目创建好之后目录结构如下
app.py:一般包含了Pecan应用的入口,包含初始化代码。
Config.py : 包含Pecan的应用配置,会被 app.py使用。
Controllersl : 这个目录包含所有的控制器,也就是API具体逻辑的地方 。
Cotrollers/root.py : 这个包含根路径对应的控制器。
Controllers/v1/ 这个目录放的是版本的API的。
Public : 文件夹存放一些Web应用所需的Image,Css或者JavaScript。
setup.py和setup.cfg用于Web应用的安装部署。
templates:存储Html或者Json的末班文件。
tests:存放测试用例。
代码变少了:application的配置
Pecan的配置很容易,通过 一个python的源码式的配置文件就可以完成基本的配置,这个配置的主要目的是指定应用程序的root,然后用于生成WSGI application。我们来看Magnum项目的列子,Magnum项目有个API服务是 用Pecan实现的,在magnum/api/config.py文件中可以找到这个文件,主要内容如下:
app = {
'root': 'magnum.api.controllers.root.RootController',
'modules': ['magnum.api'],
'debug': False,
'hooks': [
hooks.ContextHook(),
hooks.RPCHook(),
hooks.NoExceptionTracebackHook(),
],
'acl_public_routes': [
'/'
],
}
上面这个app对象就是Pecan的配置,每个Pecan应用都需要有这么一 个名为app的配置。app配置中最重要的就是root的值,这个值表示了应用程序的入口,也就是从哪个地方开始解析HTTP的根path:/。 hooks对应的配置是一些pecan的hook,作用类似于WSGI Middleware。有了app配置后,就可以让Pecan生成一个WSGI application。在Magnum项目中,magnum/api/app.py文件就是生成WSGI application的地方,我们来看一下这个主要的内容:
def get_pecan_config():
# Set up the pecan configuration
filename = api_config.__file__.replace('.pyc', '.py')
return pecan.configuration.conf_from_file(filename) def setup_app(config=None):
if not config:
config = get_pecan_config() app_conf = dict(config.app) app = pecan.make_app(
app_conf.pop('root'),
logging=getattr(config, 'logging', {}),
wrap_app=middleware.ParsableErrorMiddleware,
**app_conf
) return auth.install(app, CONF, config.app.acl_public_routes)
get_pecan_config()方法读取我们上面提到的config.py文件,然后返回一个pecan.configuration.Config对象,setup_app()函数首先调用get_pecan_config()函数获取application的配置,然后调用pecan.make_app()函数创建了一个WSGI application,调用了auth.install()函数(也就是magnum.api.auth.install()函数)为刚刚生成的WSGI application加上keystone的认证中间件(确保所有的请求都会通过keystone认证)。
到这边为止,一个pecan的WSGI application就已经准备好了,只要调用这个setup_app()函数就获得,至于如何部署这个WSGI application请参考WSGI简介这篇文章(https://segmentfault.com/a/1190000003069785)从Magnum这个实际的列子可以看出,使用了pecan之后 ,我们不再需要自己写那些WSGI application代码了,直接调用pecan的make_app()函数就能完成 这些工作,另外,对于之前使用pasteDeploy时用到的很多WSGI中间件,可以选择使用pecan的hooks机制来实现,也选择使用WSGI中间件的方式来实现,在Magnum的API服务就同时使用了这两种方式,其实pecan还可以和pastedeploy一起使用,ceilometer项目就是这么做的,大家可以看看。
确定路由变得容易了:对象分发式的路由
Pecan不仅缩减了生成WSGI application的代码,而且也让开发人员更容易的指定一个application的路由,Pecan采用了一种对象分发风格(object-dispatch style)的路由模式,我们直接通过列子来解释这种路由模式,还是以Magnum项目为例。
上面提到了,Magnum的API服务的root是magnum.api.controllers.root.RootController。这里的RootController的是一个类,我们来看代码:
class RootController(rest.RestController): _versions = ['v1']
"""All supported API versions""" _default_version = 'v1'
"""The default API version""" v1 = v1.Controller() @expose.expose(Root)
def get(self):
# NOTE: The reason why convert() it's being called for every
# request is because we need to get the host url from
# the request object to make the links.
return Root.convert() @pecan.expose()
def _route(self, args):
"""Overrides the default routing behavior. It redirects the request to the default version of the magnum API
if the version number is not specified in the url.
""" if args[] and args[] not in self._versions:
args = [self._default_version] + args
return super(RootController, self)._route(args)
别看这个类这么长,我来解释下你就懂了,首先你可以忽略掉_route()函数,这个函数使用来覆盖Pecan的默认路由实现的,在这里去掉它不妨碍我们理解Pecan(这里的_route()函数的作用把所有请求重定向到默认的API版本去),去掉_route()和其他的东西后,整个类就是变成这么短:
class RootController(rest.RestController):
v1 = v1.Controller() @expose.expose(Root)
def get(self):
return Root.convert()
首先,你要记住,这个RootController对应的是URL中根路径,也就是path中最左边的/。
RootController继承自rest.RestController,是Pecan实现的RESTful控制器,这里get()函数表示,当访问的是GET/时,由该函数处理,get()函数会返回一个WSME对象,表示已个形式的HTTP Response,这个下面再讲。get()函数上面的expose装饰器是Pecan实现路由控制的一个方式,被expose的函数才会被路由处理。
这里的v1 = v1.Controller()表示,当访问的是GET/v1或者GET/v1/....时,请求由一个v1.Controller实例来处理。
为了加深大家的理解,我们再来看下v1.Controller的实现:
class Controller(rest.RestController):
"""Version 1 API controller root.""" bays = bay.BaysController()
baymodels = baymodel.BayModelsController()
containers = container.ContainersController()
nodes = node.NodesController()
pods = pod.PodsController()
rcs = rc.ReplicationControllersController()
services = service.ServicesController()
x509keypairs = x509keypair.X509KeyPairController()
certificates = certificate.CertificateController() @expose.expose(V1)
def get(self):
return V1.convert() ...
上面这个Controoler也是继承自restRestController。所以它的get函数表示,当访问的是GET/v1的时候,要做的处理。然后它还有很多类属性,这些属性分别表示不同的URL路径的控制器:
1、/vq/bays 由bays处理
2、/v1baymodels 由baymodels处理
3、/v1/containers 由containers处理
其他的都是类似的。我们在继续看bay.B安阳市Controller的代码:
class BaysController(rest.RestController):
"""REST controller for Bays."""
def __init__(self):
super(BaysController, self).__init__() _custom_actions = {
'detail': ['GET'],
} def get_all(...): def detail(...): def get_one(...): def post(...): def patch(...): def delete(...):
这个Controller中只有函数,没有任何类属性,而且没有实现任何特殊方法,所以/v1/bays开头的URL处理都在这个controller中终结,这个类会处理如下请求:
1、GET /v1/bays
2、GET /v1/bays/{UUID}
3、POST /v1/bays
4、PATCH /v1/bays/{UUID}
5、DELETE /v1/bays/{UUID}
6、GET / v1/bays/detail/{UUID}
看到上面的3个controller之后,你应该能大概明白Pecan是如何对URL进行路由的,这种路由方式就是对象分发:(根据类属性)、(包括数据属性)和方法属性来决定如何路由一个HTTP请求,Pecan的文档中请求额路由有专门的描述,要想掌握Pecan的路由还是要完整的看一下官方文档。
内置RESTful支持
我们上面举例的controller都是继承自pecan.rest.RestController,这种controller称为RESTful controller,专门用于实现RESTful API的,因此在Openstack中使用特别多,Pecan还支持普通的controller,称为Generic controller。Generic controller继承自object对象,默认没有实现对RESTful请求的方法。简单的说,RESTful controller帮我们规定好了get_one(),get_all(),get(),post()等方法对应的HTTP请求,而Generic controller则没有,关于这两种controller的区别 ,可以看官方文档,有很清楚的示例。
对于RestController中没有预先定义好的方法,我们可以通过控制器的_custom_actions属性来指定其能处理的方法。
class RootController(rest.RestController):
_custom_actions = {
'test': ['GET'],
} @expose()
def test(self):
return 'hello'
上面这个控制器是一个根控制器,指定了/test路径支持GET方法,效果如下:
$ curl http://localhost:8080/test
hello%
那么HTTP请求和HTTP响应呢?
wsme
Pecan对请求和响应的处理
在开始提到WSME之前,我们吸纳来看下Pecan自己对HTTP请求和响应的处理。这样你能更好的理解为什么会引入WSME库。
Pecan框架为每个线程维护了单独的请求和响应的对象,你可以直接在处理函数中访问。
pecan.requesr和pecan.response分别代表当前需要处理的请求和响应对象,你可以直接操作这两个对象,比如指定响应的状态码,就像下面这个列子一样:
@pecan.expose()
def login(self):
assert pecan.request.path == '/login'
username = pecan.request.POST.get('username')
password = pecan.request.POST.get('password') pecan.response.status =
pecan.response.text = 'Bad Login!'
这个列子演示了访问POST请求的参数以及返回403,你也可以重新构造一个pecan.Response对象作为返回值:
from pecan import expose, Response class RootController(object): @expose()
def hello(self):
return Response('Hello, World!', )
另外,HTTP请求参数的参数也会可以作为控制器方法的参数,还是来看几个官方文档的列子:
class RootController(object):
@expose()
def index(self, arg):
return arg @expose()
def kwargs(self, **kwargs):
return str(kwargs)
这个控制器中的方法直接返回了参数,演示了对GET请求参数的处理,效果是这样的:
$ curl http://localhost:8080/?arg=foo
foo
$ curl http://localhost:8080/kwargs?a=1&b=2&c=3
{u'a': u'', u'c': u'', u'b': u''}
有时候,参数也可能是URL的一部分,比如最后一段path作为参数,就像下面这样:
class RootController(object):
@expose()
def args(self, *args):
return ','.join(args)
效果是这样的:
$ curl http://localhost:8080/args/one/two/three
one,two,three
另外,我们还要看一下POST方法的参数 如何处理:
class RootController(object):
@expose()
def index(self, arg):
return arg
效果如下,就是把HTTP body解析成了控制器方法的参数:
$ curl -X POST "http://localhost:8080/" -H "Content-Type:
application/x-www-form-urlencoded" -d "arg=foo" foo
返回JSON还是HTML?
如果你不是明确的返回一个Response对象,那么Pecan中方法的返回内容类型就是由expose()装饰器决定的,默认情况下,控制器的方法返回的content-type是HTML。
class RootController(rest.RestController):
_custom_actions = {
'test': ['GET'],
} @expose()
def test(self):
return 'hello'
效果如下:
$ curl -v http://localhost:8080/test
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port (#)
> GET /test HTTP/1.1
> User-Agent: curl/7.38.
> Host: localhost:
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Date: Tue, 15 Sep 2015 14:31:28 GMT
< Server: WSGIServer/0.1 Python/2.7.9
< Content-Length: 5
< Content-Type: text/html; charset=UTF-8
<
* Closing connection 0
hello%
也可以让他返回JSON:
class RootController(rest.RestController):
_custom_actions = {
'test': ['GET'],
} @expose('json')
def test(self):
return 'hello'
效果如下:
curl -v http://localhost:8080/test
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port (#)
> GET /test HTTP/1.1
> User-Agent: curl/7.38.
> Host: localhost:
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Date: Tue, 15 Sep 2015 14:33:27 GMT
< Server: WSGIServer/0.1 Python/2.7.9
< Content-Length: 18
< Content-Type: application/json; charset=UTF-8
<
* Closing connection 0
{"hello": "world"}%
甚至,你可以让一个控制器方法根据URL path的来决定是返回HTML还是JSON:
class RootController(rest.RestController):
_custom_actions = {
'test': ['GET'],
} @expose()
@expose('json')
def test(self):
return json.dumps({'hello': 'world'})
返回JSON:
$ curl -v http://localhost:8080/test.json
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port (#)
> GET /test.json HTTP/1.1
> User-Agent: curl/7.38.
> Host: localhost:
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Date: Wed, 16 Sep 2015 14:26:27 GMT
< Server: WSGIServer/0.1 Python/2.7.9
< Content-Length: 24
< Content-Type: application/json; charset=UTF-8
<
* Closing connection 0
"{\"hello\": \"world\"}"%
返回HTML:
$ curl -v http://localhost:8080/test.html
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port (#)
> GET /test.html HTTP/1.1
> User-Agent: curl/7.38.
> Host: localhost:
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Date: Wed, 16 Sep 2015 14:26:24 GMT
< Server: WSGIServer/0.1 Python/2.7.9
< Content-Length: 18
< Content-Type: text/html; charset=UTF-8
<
* Closing connection 0
{"hello": "world"}%
这里要注意一下;
1、同一个字符串作为JSON返回和作为HTML返回是不一样的,仔细看一下HTTP响应的内容。
2、我们的列子中在URL的最后加上了.html后缀或者.json后缀,请尝试一下不加后缀的变化是返回什么?然后,调换一下两个expose()的顺序再试一下。
从上面的列子可以看出,决定响应类型的主要是传递给expose()函数的参数,我们看下expose()函数的完整声明:
pecan.decorators.expose(template=None,
content_type='text/html',
generic=False)
template参数用来指定返回值得末班,如果是json就会返回json内容,这里可以指定一个
HTML文件,或者指定一个mako模板。
content_type指定响应的content-type,默认值是"text/html"
generic参数表明该方法是一个"泛型"方法,可以指定多个不同的函数对应同一个路径的不同的HTTP方法。
看过参数的解释后,你应该能大概了解expose()函数是如何控制HTTP响应的内容和类型的。
来杯咖啡看Pecan的更多相关文章
- 如何获取(GET)一杯咖啡——星巴克REST案例分析
英文原文:How to GET a Cup of Coffee 我们已习惯于在大型中间件平台(比如那些实现CORBA.Web服务协议栈和J2EE的平台)之上构建分布式系统了.在这篇文章里,我们将采取另 ...
- 来杯咖啡-装饰者模式(Decorator)
前言 上篇[观察者模式]发布已经近一个月了,个人感觉反应并不太理想,因为大家响应都不是很积极,不知是文章那里写得有问题,而且也没有人提出过有价值的改进建议,多少感觉有些失望L!因为工作繁忙,所以不可能 ...
- 西方教育骗局,终于明白精英和普通人的残酷差别!(该校流传着一个数字——4。即“4小时睡眠、4杯咖啡、GPA4.0”——要想获得满分为4分的成绩,每天只睡4个小时,困了就喝4大杯咖啡)
2018-02-14 00:00英国/私立学校 你不知道的是:西方教育通过一个宽松的过程,偷偷完成了社会分层. 1 “中国学生真是太苦了!”我的同学李女士总是发出这样的感慨. 李女士是我中学同学,在一 ...
- [mk] 喝一杯咖啡, 写一写 Makefile
Makefile 是 Linux 下组织程序的一个工具,它的命令是 make. (首字母M/m都可以) [Makefile] Makefile 编写的主旋律: target: [dependency] ...
- [GNU] 喝一杯咖啡, 写一写 Makefile
Makefile 是 Linux 下组织程序的一个工具,它的命令是 make. (首字母M/m都可以) [Makefile] Makefile 编写的主旋律: target: [dependency] ...
- Python伪开发者对于搜狐云景的测评
Python伪开发者对于搜狐云景的测评 本人是GAE和OpenShift的狂热爱好者,玩过各种国外PaaS.某次想搞个稍微复杂点的Python Web程序,需要比较好的网络传输速度,就试图找前PM(P ...
- 【PhotoShop】采用PS让美丽的咖啡泡沫
稀土一杯咖啡,如何你不能击败张(常苦黑咖啡饮料实在受不了! ) 得到例如以下图 看着还不错,但是总感觉空空荡荡的,所以就拿来PS练手了.终于效果图例如以下: 以下讲下制作过程: 首先是给照片加下咖啡泡 ...
- “浪潮杯”山东省第五届ACM大学生程序设计竞赛(总结贴)
第一次參加省赛有点小激动,尽管是作为打星队參赛,但心情却是上下起伏. 5月9号晚上11点多到威海,有点略冷.可是空气比淄博好多了,大家到了旅馆的时候都非常晚了,抱怨了一下三星级的酒店的待遇,喝杯咖啡早 ...
- 你看不懂的spring原理是因为不知道这几个概念
背景 问题从一杯咖啡开始. 今天我去楼下咖啡机买了一杯「粉黛拿铁」.制作过程中显示: 我取了做好的粉黛拿铁,喝了一口,果然就是一杯热巧克力.咦咦咦,说好的拿铁呢?虽然我对「零点吧」的咖啡评价很高,觉得 ...
随机推荐
- Python实现浏览器自动化操作
Python实现浏览器自动化操作 (2012-08-02 17:35:43) 转载▼ 最近在研究网站自动登录的问题,涉及到需要实现浏览器自动化操作,网上有不少介绍,例如使用pamie,但是只是 ...
- Pandas数据处理实战:福布斯全球上市企业排行榜数据整理
手头现在有一份福布斯2016年全球上市企业2000强排行榜的数据,但原始数据并不规范,需要处理后才能进一步使用. 本文通过实例操作来介绍用pandas进行数据整理. 照例先说下我的运行环境,如下: w ...
- Unreal Engine 4(虚幻UE4)GameplayAbilities 插件入门教程(四)技能屏蔽和简单的Buff等
本节内容继续上一节教程的内容(如果没有看过前面的教程,请前往学习),不会讲太难的新东西,而是继续探究技能标签(Abiilty Tags)的内容.先来一道开胃菜. 第1.1步: 将上一次的召唤冰龙中的C ...
- 集群之mysql主从配置(windows和linux版)
起因 由于网站进一步开发运行的需求,要求主机7*24小时运行正常,同时要求能够防止数据库灾难.考虑到后期的开发程度和业务量,准备向高可用系统进行改变,同时通过负载均衡提高网络性能.于是第一步就考虑到了 ...
- 用js写一个回车键盘事件
用js来监听键盘事件,代码如下: <script type="text/javascript" language=JavaScript charset="UTF-8 ...
- docker~使用阿里加速器安centos
回到目录 上一篇说了hub.docker.com里拉个镜像太,而阿里云为我们做了不少本国镜像,这样下载的速度就很惊人了,下面看一下在centos7下配置阿里云加速器的方法 打开服务配置文件 vi /e ...
- 20170713_filter/sort
js:filter过滤数组元素 //1.数组取奇数 var arr = [1,2,3,4,5]; var r = arr.filter(function(x){ return x % 2 !== 0; ...
- [luogu P3801] 红色的幻想乡 [线段树][树状数组]
题目背景 蕾米莉亚的红雾异变失败后,很不甘心. 题目描述 经过上次失败后,蕾米莉亚决定再次发动红雾异变,但为了防止被灵梦退治,她决定将红雾以奇怪的阵势释放. 我们将幻想乡看做是一个n*m的方格地区,一 ...
- SetConsoleTextAttribute 函数--设置控制台文本属性
SetConsoleTextAttribute函数 来源:https://msdn.microsoft.com/en-us/library/windows/desktop/ms686047(v=vs. ...
- Angular02 将数据添加到组件中
准备:已经搭建好angular-cli环境.知道如何创建组件 一.将一个数据添加到组件中 1 创建一个新的组件 user-item 2 将组件添加到静态模板中 3 为组件添加属性,并利用构造器赋值 4 ...