python pecan api开发_python学习之pecan框架

Pecan框架

基础知识介绍:

一. 文件中需要包含一个config.py文件,该文件用于标注pecan程序的起点等配置信息:

app = {

'root': 'webdemo.api.controllers.root.RootController',

'modules': ['webdemo.api'],

'debug': True,

}

modules:其中包含的python包,是app.py文件所在的包,即setup_app方法所在的包

root:即RootController所在的路径,/路径

debug:是否开启调试,生产环境的话,将其置为False

二. pecan可以实现对象分发式的路由

当RootController继承pecan.rest.RestController时,存在URL映射关系如下表所示,当然也可以另外自己定义新的方式:

Method

Description

Example Method(s) / URL(s)

get_one

Display one record.

GET /books/1

get_all

Display all records in a resource.

GET /books/

get

A combo of get_one and get_all.

GET /books/ 或 GET /books/1

new

Display a page to create a new resource.

GET /books/new

edit

Display a page to edit an existing resource.

GET /books/1/edit

post

Create a new record.

POST /books/

put

Update an existing record.

POST /books/1?_method=put 或 PUT /books/1

get_delete

Display a delete confirmation page.

GET /books/1/delete

delete

Delete an existing record.

POST /books/1?_method=delete 或 DELETE /books/1

在RootController类中,一般会写上如下的代码:

from pecan import rest

import pecan

class RootController(rest.RestController):

@pecan.expose()

def get(self):

return 'This is RootController GET.'

当存在以上代码时,使用以下命令调用就会有返回值:

curl -X GET http://127.0.0.1:8080

返回值为:

This is RootController GET.

也就是说,curl中的GET对应了代码中的get()方法,若要增加POST方法,可以在代码中添加如下:

@pecan.expose()

def post(self):

return 'This is RootController POST.'

当然,如果有的方法,需要既可以使用GET,又可以使用POST,且对不同的方法有不同的回应,则可以写成如下:

from pecan import rest

import pecan

class RootController(rest.RestController):

_custom_actions = {

'test': ['GET', 'POST'],

}

@pecan.expose()

def test(self):

if pecan.request.method == 'POST':

return 'This is RootController test POST.'

elif pecan.request.method == 'GET':

return 'This is RootController test GET.'

可以使用如下命令进行请求:

curl -X GET http://127.0.0.1:8080/test

curl -X POST http://127.0.0.1:8080/test

==下面重点介绍对象分发式路由:==

当存在以下代码的时候:

class v1Controller(rest.RestController):

@pecan.expose()

def get(self):

return 'This is v1Controller GET.'

class RootController(rest.RestController):

v1 = v1Controller()

在RootController类中,生成了一个v1Controller对象,这个对象就是用来做分发式路由的,因此可以使用如下命令去调用它:

curl -X GET http://127.0.0.1:8080/v1

三. 使用WSME来规范API的响应值

wsme模块可以用来规范API的请求和响应值,并且可以和pecan模块很好的结合在一起,其支持的类型如下:

Type

Json type

str

String

unicode

String

int

Number

float

Number

bool

Boolean

Decimal

String

date

String (YYYY-MM-DD)

time

String (hh:mm:ss)

datetime

String (YYYY-MM-DDThh:mm:ss)

Arrays

Array

None

null

Complex types

Object

在下面这个例子中,可以看出:

from wsmeext.pecan import wsexpose

from pecan import rest

class RootController(rest.RestController):

@wsexpose(int, int)

def get_one(self, arg):

return 1

@wsexpose(int, int)中第一个int表示返回值必须为int,第二个int表示请求参数必须为int

在这个例子中,可以使用curl去测试:

curl -X GET http://127.0.0.1:8081/1

# 返回消息如下:

1

若用以下的输入,则会错误:

curl -X GET http://127.0.0.1:8081/xxx

# 返回消息如下:

{"debuginfo": null, "faultcode": "Client", "faultstring": "Invalid input for field/attribute arg. Value: 'xxx'. unable to convert to int. Error: invalid literal for int() with base 10: 'xxx'"}

当然,wsme还可以用来检测更为复杂的类型,如类对象,这个在下面再做介绍。

四. wsme检测类对象:

在openstack中,使用Rest API返回的响应值经常会是以下格式:

{"users":[{"name":"Alice","age":30},{"name":"Bob","age":40}]}

针对于这种情况,我们可以使用WSME的自定义类型,下面就定义一个user类型和users类型:

from wsme import types as wtypes

class User(wtypes.Base):

name = wtypes.text

age = int

class Users(wtypes.Base):

users = [User]

注意: 这里没有使用__init__是因为,父类的初始化方法参数为**kw,因此没有特殊需求,这里可以不写

现在可以使用如下的方法去调用这两个类型:

from wsmeext.pecan import wsexpose

from pecan import rest

class RootController(rest.RestController):

@wsexpose(User, int)# 返回值为User类对象

def get_one(self, id):

if id == 1:

user_info = {

'name': 'yangsijie',

'age': 23

}

return User(**user_info)

# 或者也可以使用下面这种方式:

# if id == 1:

# return User(name='yangsijie', age=23)

@wsexpose(Users)# 返回值为Users类对象

def get_all(self):

user_info_list = [

{

'name': 'yangsijie',

'age': 23

},

{

'name': 'panna',

'age': 23

}

]

users_list = [User(**user_info) for user_info in user_info_list]

return Users(users=users_list)

# 或者也可以使用下面这种方式:

# return Users(users=[User(name='yangsijie', age=23), User(name='panna', age=23)])

现在使用curl命令会出现以下现象:

curl http://127.0.0.1:8081/1

# 返回值为:

{"age": 23, "name": "yangsijie"}

curl http://127.0.0.1:8081

# 返回值为:

{"users": [{"age": 23, "name": "yangsijie"}, {"age": 23, "name": "panna"}]}

WSME还可以用于检测上传的参数是否为complex type类型(由于上传要用到POST操作,所以此处就以POST作例子):

class RootController(rest.RestController):

@wsexpose(None, body=User)# 检查参数必须为User类对象

def post(self, user):

print user.name

print user.age

当使用curl访问时,程序的控制台会打印出访问的值:

curl -X POST http://127.0.0.1:8081 -H "Content-Type: application/json" -d '{"name": "yangsijie", "age": 30}'

# 程序控制台显示的是:

yangsijie

30

这里需要注意的是,如果这里不写成body=User,而是直接写成User,那么curl中的data字段就也需要进行相应的修改,需要用以下命令来实现:==经测试,这种方法好像根本行不通????==

curl -X POST http://127.0.0.1:8081 -H "Content-Type: application/json" -d '{"user": {"name": "yangsijie", "age": 30}}'

当类中的属性没有传入值的时候,那么这个类变量是wsme.types.UnsetType对象:

from wsme import types as wtyps

user = User(name='test1')# 这里没有给user对象的age赋值

if user.age is wtypes.Unset:

return True

结果会返回True,因为此处的age为wsme.types.UnsetType类型。

五. 使用wsme设置status_code

使用wsme默认返回的状态码为200、400和500。

状态码

含义

200

成功

400

客户端输入出错(参数错误等等)

500

服务器端错误

如果将之前的例子做略微的修改,可以让其返回的状态值不一样:

from wsmeext.pecan import wsexpose

from pecan import rest

class RootController(rest.RestController):

@wsexpose(int, int, status_code=201)# 之前如果成功的话,返回的是200,现在将其改为201

def get_one(self, arg):

return 1

在现在这个例子中,使用如上一样的curl命令,会发现返回的状态码变成了201

但是在平时的使用中,我们肯定需要根据不同的判断,返回不同的status_code,那怎么办呢?我们可以使用如下的方式去实现:

使用wsme.api.Response在返回值的同时指定status_code

抛出wsme.exc.ClientSideError错误的同时,指定错误原因及status_code

抛出自定义错误,并且在自定义错误中指定错误原因及status_code

后面两种方式,是遇到错误的时候返回的

下面就看看例子中究竟是如何使用的:

import wsme

from wsme import types as wtypes

from wsmeext.pecan import wsexpose

from pecan import rest

class BookNotFound(Exception):# 自定义错误

message = 'Book with ID={id} Not Found'# 定义错误原因

code = 404# 定义status_code

def __init__(self, id):

message = self.message.format(id=id)

super(BookNotFound, self).__init__(message)

class Book(wtypes.Base):

id = int

name = wtypes.text

class BookController(rest.RestController):

@wsexpose(Books, int)

def get_one(self, id):

if id == 1:

raise BookNotFound(id=id)# 使用自定义错误返回

elif id == 2:

raise wsme.exc.ClientSideError('ID:\'1\'is wrong!!!', status_code=403)# 使用wsme.exc.ClientSideError抛出错误

else:

return wsme.api.Response(Books(), status_code=204)# 使用wsme.api.Response返回值

案例:

该项目实现了用户的查找,添加和删除等(没有真正的结合数据库实现,只是一个demo)

项目的架构图如下:

webdemo2/

├── api

│   ├── app.py# 存放WSGI application的入口

│   ├── config.py# 存放Pecan的配置

│   ├── controllers# 存放Pecan控制器的代码

│   │   ├── __init__.py

│   │   ├── root.py

│   │   └── v1

│   │   ├── controller.py

│   │   ├── __init__.py

│   │   └── users.py

│   ├── expose.py

│   └── __init__.py

├── cmd

│   ├── api.py

│   └── __init__.py

└── __init__.py

本项目实现了以下几个功能:

GET/v1/users获取所有用户的列表

POST/v1/users创建一个用户

GET/v1/users/获取一个指定用户的详细信息

PUT/v1/users/修改一个指定用户的详细信息

DELETE/v1/users/删除一个指定用户

POST/v1/users//kill杀死一个指定用户

api/app.py:

import pecan

from webdemo2.api import config as api_config

def get_pecan_config():

filename = api_config.__file__.replace('.pyc', '.py') # get the absolute path of the pecan config.py

return pecan.configuration.conf_from_file(filename)

def setup_app(): # the main functhing, start listening

config = get_pecan_config()

app_conf = dict(config.app)

app = pecan.make_app(

app_conf.pop('root'),

logging=getattr(config, 'logging', {}),

**app_conf)

return app

api/config.py:

app = {

'root': 'webdemo2.api.controllers.root.RootController',

'modules': ['webdemo2.api'],

'debug': True,

}

api/expose.py:

import wsmeext.pecan as wsme_pecan

def expose(*args, **kwargs):

if 'rest_content_types' not in kwargs:

kwargs['rest_content_types'] = ('json',)

return wsme_pecan.wsexpose(*args, **kwargs)

该函数用来让API返回JSON格式的数据

api/controllers/root.py:

from pecan import rest, expose

from wsme import types as wtypes

from webdemo2.api.controllers.v1 import controller as v1_controller

from webdemo2.api.expose import expose as wsexpose

class RootController(rest.RestController):

# All supported API versions

_versions = ['v1']

# The default API version

_default_version = 'v1'

v1 = v1_controller.V1Controller()

@wsexpose(wtypes.text)

def get(self):

return 'webdemo2'

@expose()

def _route(self, args, request=None):

"""When the API version is not specified in the url, v1 is used as the default version."""

if args[0] and args[0] not in self._versions:

args = [self._default_version] + args

return super(RootController, self)._route(args)

当URL中未指定版本号时,_route函数将版本号默认置为v1

api/controllers/v1/controller.py:

from pecan import rest

from wsme import types as wtypes

from webdemo2.api.expose import expose as wsexpose

from webdemo2.api.controllers.v1.users import UsersController

class V1Controller(rest.RestController):

users = UsersController()

@wsexpose(wtypes.text)

def get(self):

return 'webdemo2 v1controller'

api/controllers/v1/users.py:

from wsme import types as wtypes

from pecan import rest, expose

from webdemo2.api.expose import expose as wsexpose

class User(wtypes.Base):

id = wtypes.wsattr(wtypes.text, mandatory=True)

name = wtypes.text

age = int

class Users(wtypes.Base):

users = [User]

class UsersController(rest.RestController):

# HTTP GET /users/

@wsexpose(Users)

def get(self):

user_info_list = [

{

'id': '1',

'name': 'Alice',

'age': 30

},

{

'id': '2',

'name': 'Bob',

'age': 40

}

]

users_list = [User(**user_info) for user_info in user_info_list]

return Users(users=users_list)

# HTTP POST /users

@wsexpose(None, body=User, status_code=201)

def post(self, user):

print user

@expose()

def _lookup(self, user_id, *remainder):

return UserController(user_id), remainder

class UserController(rest.RestController):

_custom_actions = {

'kill': ['POST']

}

def __init__(self, user_id):

self.user_id = user_id

# HTTP GET /users/123456/

@wsexpose(User)

def get(self):

user_info = {

'id': self.user_id,

'name': 'Alice',

'age': 30

}

return User(**user_info)

# HTTP PUT /users/123456/

@wsexpose(User, body=User)

def put(self, user):

user_info = {

'id': self.user_id,

'name': user.name,

'age': user.age + 1

}

return User(**user_info)

# HTTP DELETE /users/123456/

@wsexpose()

def delete(self):

print ('Delete user_id:%s' % self.user_id)

# HTTP POST /users/123456/kill

@wsexpose(status_code=202)

def kill(self):

print ('Kill user_id:%s' % self.user_id)

cmd/api.py:

from wsgiref import simple_server

from webdemo2.api import app

def main():

application = app.setup_app()

srv = simple_server.make_server('', 8081, application)

print ('Server on port 8081, listening...')

srv.serve_forever()

if __name__ == '__main__':

main()

运行该文件,即可开始监听

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值