Pecan

Pecan是一个提供对象分派式路由 (object-dispatch style routing) 的轻量级 web 框架。

尽管它很轻巧,但 Pecan 确实提供了广泛的功能集,可用于构建基于 HTTP 的应用程序,包括:

  • Object-dispatch for easy routing

  • Full support for REST-style controllers

  • Extensible security framework

  • Extensible template language support

  • Extensible JSON support

  • Easy Python-based configuration

@expose()

如果一个方法被@expose()装饰,那么Pecan会将相应的请求路由到这个方法。

示例:

from pecan import expose

class RootController(object):
    @expose('json')
    def hello(self):
        return {'msg': 'Hello!'}

当访问/hello时,会返回{'msg': 'Hello!'}给客户端。

@expose的参数 “json”,指定content_typeapplication/json。如果不指定,content_type默认是text/html

路由

Pecan 使用称为对象分发(object-dispatch)的路由策略(routing strategy)将 HTTP 请求映射到控制器,然后将方法回调。对象分发首先将路径拆分为组件列表,然后从根控制器开始遍历对象路径。

示例:

from pecan import expose

class BooksController(object):
    @expose()
    def index(self):
        return "Welcome to book section."

    @expose()
    def bestsellers(self):
        return "We have 5 books in the top 10."

class CatalogController(object):
    @expose()
    def index(self):
        return "Welcome to the catalog."

    books = BooksController()

class RootController(object):
    @expose()
    def index(self):
        return "Welcome to store.example.com!"

    @expose()
    def hours(self):
        return "Open 24/7 on the web."

    catalog = CatalogController()

在线商店对 /catalog/books/bestsellers 的请求分为 catalogbooksbestsellers。接下来,Pecan 将在根控制器上查找 catalog。然后在 catalog 对象中查找 books,最终在books中找到 bestsellers方法。

路径:

└── /
    ├── /hours
    └── /catalog
         └── /catalog/books
            └── /catalog/books/bestsellers

根据路径路由到以下控制器方法:

└── RootController.index
    ├── RootController.hours
    └── CatalogController.index
         └── BooksController.index
            └── BooksController.bestsellers

使用通用Controller写API

在下面的示例中,使用普通的Python对象创建controller。

GET /请求进来时,会路由到RootControllerindex方法。而这个index方法被@expose(generic=True, template='json')装饰,在expose内部生成@index.when。此时,如果请求路径相同,但是请求方法不同,那么Pecan会根据请求方法将请求路由到相应的被@index.when装饰的方法。

from pecan import abort, expose

# Note: this is *not* thread-safe.  In real life, use a persistent data store.
BOOKS = {
    '0': 'The Last of the Mohicans',
    '1': 'Catch-22'
}


class BookController(object):

    def __init__(self, id_):
        self.id_ = id_
        assert self.book

    @property
    def book(self):
        if self.id_ in BOOKS:
            return dict(id=self.id_, name=BOOKS[self.id_])
        abort(404)

    # HTTP GET /<id>/
    @expose(generic=True, template='json')
    def index(self):
        return self.book

    # HTTP PUT /<id>/
    @index.when(method='PUT', template='json')
    def index_PUT(self, **kw):
        BOOKS[self.id_] = kw['name']
        return self.book

    # HTTP DELETE /<id>/
    @index.when(method='DELETE', template='json')
    def index_DELETE(self):
        del BOOKS[self.id_]
        return dict()


class RootController(object):

    @expose()
    def _lookup(self, id_, *remainder):
        return BookController(id_), remainder

    # HTTP GET /
    @expose(generic=True, template='json')
    def index(self):
        return [dict(id=k, name=v) for k, v in BOOKS.items()]

    # HTTP POST /
    @index.when(method='POST', template='json')
    def index_POST(self, **kw):
        id_ = str(len(BOOKS))
        BOOKS[id_] = kw['name']
        return dict(id=id_, name=kw['name'])

使用RestController写API

Pecan提供RestController,开发者可以继承这个类来创建controller。

from pecan import expose
from pecan.rest import RestController

from mymodel import Book

class BooksController(RestController):

    @expose()
    def get(self, id):
        book = Book.get(id)
        if not book:
            abort(404)
        return book.title

RestController提供如下的映射。

例如:GET /books/1请求会路由到get_one方法。

MethodDescriptionExample Method(s) / URL(s)
get_oneDisplay one record.GET /books/1
get_allDisplay all records in a resource.GET /books/
getA combo of get_one and get_all.GET /books/
GET /books/1
newDisplay a page to create a new resource.GET /books/new
editDisplay a page to edit an existing resource.GET /books/1/edit
postCreate a new record.POST /books/
putUpdate an existing record.POST /books/1?_method=put
PUT /books/1
get_deleteDisplay a delete confirmation page.GET /books/1/delete
deleteDelete an existing record.POST /books/1?_method=delete
DELETE /books/1

如果需要自定义方法可以定义_custom_actions

如下示例:请求POST /books/checkout会路由到BooksControllercheckout方法。

from pecan import expose
from pecan.rest import RestController

from mymodel import Book

class BooksController(RestController):

    _custom_actions = {
        'checkout': ['POST']
    }

    @expose()
    def checkout(self, id):
        book = Book.get(id)
        if not book:
            abort(404)
        book.checkout()
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐