来杯咖啡看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原理是因为不知道这几个概念
背景 问题从一杯咖啡开始. 今天我去楼下咖啡机买了一杯「粉黛拿铁」.制作过程中显示: 我取了做好的粉黛拿铁,喝了一口,果然就是一杯热巧克力.咦咦咦,说好的拿铁呢?虽然我对「零点吧」的咖啡评价很高,觉得 ...
随机推荐
- Android hook神器frida(一)
运行环境 ● Python – latest 3.x is highly recommended ● Windows, macOS, or Linux安装方法使用命令 sudo pip install ...
- Groovy - 介绍
Groovy 是 用于Java虚拟机的一种敏捷的动态语言,它是一种成熟的面向对象编程语言,既可以用于面向对象编程,又可以用作纯粹的脚本语言.使用该种语言不必编写过多的代码,同时又具有闭包和动态语言中的 ...
- DL4NLP——词表示模型(一)表示学习;syntagmatic与paradigmatic两类模型;基于矩阵的LSA和GloVe
本文简述了以下内容: 什么是词表示,什么是表示学习,什么是分布式表示 one-hot representation与distributed representation(分布式表示) 基于distri ...
- Everything 使用记录
背景:在windows环境下,使用系统自带的搜索框经常出现搜索不到指定文件的问题,在网上无意发现了这款软件,真的很好用! 1 文件列表 建立文件列表主要是为了以后可以在指定的目录内查找自己想要的文件, ...
- Echarts关系图-力引导布局
需要做一个树形图,可以查看各个人员的关系. 可伸缩的力引导图-失败 刚开始,打算做一个可展开和伸缩的,搜索时候发现CSDN有一篇美美哒程序媛写的Echarts Force力导向图实现节点可折叠. 这里 ...
- JavaScript系统学习小结——Object类型、Array类型
今天学习JavaSript中引用变量中的Object类型和Array类型: 1. Js中大多数引用类型值都是Object类型的实例,Object类型在应用程序中存储和传输数据时,是非常理想的选择: 创 ...
- ORACLE - 管理表空间和数据文件
ORACLE表空间是一个逻辑分区,一个数据文件只能属于一个表空间,一个表空间可以拥有多个数据文件. 一般情况下,如果一个实例分配给多个应用使用,需要创建不同的表空间,每个用户使用自己的表空间. 一.表 ...
- JAVA 中BIO,NIO,AIO的理解以及 同步 异步 阻塞 非阻塞
在高性能的IO体系设计中,有几个名词概念常常会使我们感到迷惑不解.具体如下: 序号 问题 1 什么是同步? 2 什么是异步? 3 什么是阻塞? 4 什么是非阻塞? 5 什么是同步阻塞? 6 什么是同步 ...
- goroutine 加 channel 代替递归调用,突破递归调用的层级限制
package main import ( "fmt" "github.com/davecgh/go-spew/spew" "github.com/B ...
- rpm体系下的linux安装httpd+mysql+…
一.安装apache 在rpm体系下,apache称为httpd. yum install httpd 即可! 二.安装mysql yum install mysql 三.安装mysql-server ...