Pecan 使用介绍

发布时间: 更新时间: 总字数:1401 阅读时间:3m 作者: IP上海 分享 网址

Pecan 是一个 WSGI(Web Server Gateway Interface) 对象调度 web 框架,具有架构设计精妙、响应快速,依赖较少的特点。在 OpenStack API 框架中使用较多。

安装

pip install pecan

使用

  • 创建项目
$ pecan create test_pecan
$ cd test_pecan
$ tree . | grep -v pyc
.
├── MANIFEST.in
├── config.py
├── public
│   ├── css
│   │   └── style.css
│   └── images
│       └── logo.png
├── setup.cfg
├── setup.py
└── test_pecan
    ├── __init__.py
    ├── app.py
    ├── controllers
    │   ├── __init__.py
    │   └── root.py
    ├── model
    │   ├── __init__.py
    ├── templates
    │   ├── error.html
    │   ├── index.html
    │   └── layout.html
    └── tests
        ├── __init__.py
        ├── config.py
        ├── test_functional.py
        └── test_units.py
  • config.py 中包含 API 入口:
app = {
    'root': 'test_pecan.controllers.root.RootController',
    'modules': ['test_pecan'],
  • root:RootController 所在的路径,API 的根 / 路径
  • modules:app.py 文件所在的包,即 setup_app 方法所在的包
  • debug:是否开启调试,生产环境建议配置为 False

分发式的路由

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

示例

示例1

from pecan import expose, rest

class RootController(rest.RestController):

    _custom_actions = {
        'test': ['GET', 'POST'],
    }

    @expose()
    def get_one(self, arg):
        return 1

    @expose()
    def test(self):
        # header: pecan.request.headers.get('Content-Type')
        # querys: pecan.request.GET.items():
        if pecan.request.method == 'POST':
            return 'This is RootController POST test.'
        elif pecan.request.method == 'GET':
            return 'This is RootController GET test.'

其中:

  • _custom_actions 指定 test 方法接受的请求类型
  • @expose()指定返回值

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

$ curl -X GET http://127.0.0.1:8000/test
This is RootController GET test.

$ curl -X POST http://127.0.0.1:8000/test
This is RootController POST test.

示例2

from pecan import expose

class RootController(object):
    @expose()
    def index(self, arg):
        return arg

    @expose()
    def args(self, *args):
        return ','.join(args)

    @expose()
    def kwargs(self, **kwargs):
        return str(kwargs)
1. query string to arguments
$ curl http://127.0.0.1:8000/?arg=foo
foo
$ curl http://127.0.0.1:8000/kwargs?a=1&b=2&c=3
{u'a': u'1', u'c': u'3', u'b': u'2'}

2. remaining path to arguments
$ curl http://127.0.0.1:8000/args/one/two/three
one,two,three

3. POST body to arguments
$ curl -X POST "http://127.0.0.1:8000/" -H "Content-Type: application/x-www-form-urlencoded" -d "arg=foo"
foo
$ curl -X POST "http://127.0.0.1:8000/kwargs" -H "Content-Type: application/x-www-form-urlencoded" -d "name=foo;age=18"

WSME

WSME(Web Service Made Easy)是用于实现 REST 服务的 typing 库,一般和Pecan结合使用,如 OpenStack 的很多项目都使用了 Pecan + WSME 的组合来实现 API。

WSME 模块用来规范API的请求和响应值:

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

其中:

  • @signature: 这个装饰器用来描述一个函数的输入和输出
  • @wsexpose(int, int)中第一个int表示返回值必须为int,第二个int表示请求参数必须为int。包@wsexpose@signature 的功能,效果就像Pecan的expose装饰器

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

$ curl -X GET http://127.0.0.1:8000/1
1

示例说明

以学生类为例:

{"students": [{"name": "foo", "age": 17}, {"name": "bar", "age": 18}]}

定义 Student 类和 Students 类型:

from wsme import types as wtypes

class Student(wtypes.Base):
    name = wtypes.text
    age = int

class Students(wtypes.Base):
    students = [Student]

Post/Put Body 约束

  • Controller
class RootController(rest.RestController):

    # 检查参数必须为 Student 对象
    @wsexpose(None, body=Student)
    def post(self, stu):
        print(f'name: {stu.name}, age: {stu.age}')
  • 调用
curl -X POST http://127.0.0.1:8000 -H "Content-Type: application/json" -d '{"name": "foo2", "age": 18}'

如果 post body 不是 Student 对象,将会报错。

返回值约束

  • Controller
from wsmeext.pecan import wsexpose
from pecan import rest


class RootController(rest.RestController):

    # 返回值为Student类对象,int 为传入的参数
    @wsexpose(Student, int)
    def get_one(self, id):
        if id == 1:
            stu_info = {
                'name': 'bar',
                'age': 18
            }
            # return Student(name='bar', age=18)
            return Student(**stu_info)
        else:
            raise wsme.exc.ClientSideError('something is wrong!', status_code=403)

    @wsexpose(Students)
    def get_all(self):
        stu_info_list = [
            {
                'name': 'foo',
                'age': 17
            },
            {
                'name': 'bar',
                'age': 18
            }
        ]
        # return Students(students=[Student(name='foo', age=17), Student(name='bar', age=18)])
        return Students(students=[Student(**stu_info) for stu_info in stu_info_list])
  • 调用
$ curl http://127.0.0.1:8081/1
{"name": "bar", "age": 18}

$ curl http://127.0.0.1:8081
{"students": [{"name": "foo", "age": 17}, {"name": "bar", "age": 18}]}

其他

如果觉得 wsexpose 约束太严格,可以使用如下通用方法

  • controller
import pecan
from pecan import rest

class TestController(rest.RestController):
    @pecan.expose('json')
    def get(self):
        return {"version": "1.0.0"}

    @pecan.expose('json', int)
    def put(self, foo):
        #return "foo"  # positive control
        return pecan.request.body

    @pecan.expose('json', int)
    def post(self, foo):
        #return "foo"  # positive control
        return pecan.request.body

    @pecan.expose('json', int)
    def patch(self, foo):
        #return "foo"  # positive control
        return pecan.request.body
  • 调用
$ curl -X POST http://127.0.0.1:8080/test/1 -d '{}'
"{}"
$ curl -X PUT http://127.0.0.1:8080/test/1 -d '{}'
"{}"
$ curl -X PATCH http://127.0.0.1:8080/test/1 -d '{}'
"{}"

status_code

以下示例返回值为 201

from wsmeext.pecan import wsexpose
from pecan import rest

class RootController(rest.RestController):

    @wsexpose(int, int, status_code=201)
    def get_one(self, arg):
        return 1

自定义 expose

如果用户没有指定返回值类型,默认为 json 格式:

  • api/expose.pys
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)

分布式路由

通常

  • 代码
from pecan import expose, rest


class v1Controller(rest.RestController):
    @expose()
    def get(self):
        return 'This is v1Controller GET.'


class RootController(rest.RestController):

    v1 = v1Controller()
  • 请求
$ curl -X GET http://127.0.0.1:8000/v1
This is v1Controller GET.

_lookup

  • 代码
from pecan import expose, abort
from somelib import get_student_by_name

class StudentController(object):
    def __init__(self, student):
        self.student = student

    @expose()
    def name(self):
        return self.student.name

class RootController(object):
    @expose()
    def _lookup(self, primary_key, *remainder):
        student = get_student_by_primary_key(primary_key)
        if student:
            return StudentController(student), remainder
        else:
            abort(404)
  • 请求
# 获取 id == 8 的名称
$ curl -X GET http://127.0.0.1:8000/8/name

Templating

pecan.expose 支持的渲染模板:

  • Mako
  • Genshi
  • Kajiki
  • Jinja2
  • JSON

参考

  1. https://github.com/pecan/pecan
Home Archives Categories Tags Statistics
本文总阅读量 次 本站总访问量 次 本站总访客数