Pecan 框架学习笔记
Pecan 是一个 WSGI(Web Server Gateway Interface) 对象调度 web 框架,具有架构设计精妙、响应快速,依赖较少的特点。在 OpenStack API 框架中使用较多。
一、Pecan 工程的创建和运行
1. 安装 Pecan
使用 pip 安装 pecan:
pip install pecan
2. 创建第一个 Pecan 应用
创建虚拟环境并激活:
virtualenv pecan-env cd pecan-env source bin/activate pecan create pecandemo cd pecandemo
3. 运行 Pecan 工程
pecan serve config.py
二、RootController 模版代码分析
RootController 是 Pecan 应用程序根路径对应的控制器。新建的 Pecan 工程中,pecandemo.contronllers.root.py
中有如下模版代码:
class RootController(object): @expose(generic=True, template='index.html') def index(self): return dict() @index.when(method='POST') def index_post(self, q): redirect('https://pecan.readthedocs.io/en/latest/search.html?q=%s' % q) @expose('error.html') def error(self, status): try: status = int(status) except ValueError: # pragma: no cover status = 500 message = getattr(status_map.get(status), 'explanation', '') return dict(status=status, message=message)
逐个方法看一下:
1. index() 方法
@expose(generic=True, template='index.html') def index(self): return dict()
任何到达应用程序根目录(/
)的 HTTP GET 都将被路由到此方法。index()
方法返回一个字典。
趁此,说一下 pecan.expose
:
pecan.expose
可以标记 controller 方法,使得一个方法可以通过 HTTP 访问,主要形参有:
generic
:boolean 值。当为 True 时,相同的路径会因 HTTP 方法的不同路由到相应的方法。template
:模板。route
:指定路径段的名字,默认是函数名。
Pecan 默认采用 expose
进行路由绑定,可以使 HTTP 请求找到对应的方法。如果一个方法没有用 expose
修饰,Pecan 不会将请求路由到它。
不同的使用方法有不同的效果,举例:
expose()
class RootController(object): @expose() def hello(self): return "Hello World!"
被装饰的方法返回一个字符串,表示 HTML 响应的 body。
@expose("html_template_name")
class RootController(object): @expose("html_demo.html") def say_hello(self): return {"say": "Hello, pecan!"}
被装饰的方法返回一个 Python 字典,该字典可以在 HTML 模版中使用 ${key}
的方式引用,如 html_demo.html 如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>title</title> </head> <body> ${say} <br> </body> </html>
运行起来使用浏览器通过 URL(实例:http://127.0.0.1:8080/say_hello
)访问如下图:
![[Hello, pecan!.png|180]]
expose(route="certain-name")
默认在请求某个方法时是用方法名,例如上面就用的 http://.../say_hello
。但有时候会有将请求改为类似于 /certain-name
的需求,由于 Python 语法限制,方法并不能命名为 certain-name
,这时使用 @expose(route="certain-name")
,被装饰方法将响应 /certain-name
请求。
@expeose(generic=True)
expose()
方法中的 generic
参数当为 True
时,可以根据请求方法对 URL 进行重载,即一个 URL 路径可以被两个不同的方法处理,相同的路径会因 HTTP 方法的不同路由到相应的方法。
expose()
叠加使用
class RootController(object): @expose("json") @expose("html_demo.html", content_type="text/plain") @expose("html_demo.html") def say_hello(self): return {"say": "Hello, pecan!"}
叠加使用后一个 say_hello
方法可以响应三种格式的请求(application/json
、text/plain
和 tex t/html
)。
@expose("json")
当客户端请求 /say_hello.json
或是 http header 中否存在 Accept: application/json
时,使用 JSON 序列化响应。
访问 http://127.0.0.1:8080/say_hello.json
:
![[Pasted image 20230713113015.png|180]]
@expose("html_demo.html", content_type="text/plain")
当客户端请求 /say_hello.txt
或者 http header 中存在 Accept: text/plain
时,使用 html_demo.html 模板文件响应。
访问 http://127.0.0.1:8080/say_hello.txt
:
![[Pasted image 20230713113146.png|180]]
@expose("html_demo.html")
当客户端请求 /say_hello.html
时使用 html_demo.html
模板文件。如果客户端请求 /say_hello
并且没有显式指明内容类型,pecan 将默认使用 text/html
内容类型进行相应,假定客户端需要 HTML。
2. index_post() 方法
@index.when(method='POST') def index_post(self, q): redirect('https://pecan.readthedocs.io/en/latest/search.html?q=%s' % q)
@index.when(method='POST')
与刚才 index()
方法上的 generic=True
配合实现任何到应用程序根目录的 HTTP POST 都将路由到该方法。
index_post()
方法接收一个 HTTP POST 参数(q
)。
3. error() 方法
@expose('error.html') def error(self, status): try: status = int(status) except ValueError: # pragma: no cover status = 500 message = getattr(status_map.get(status), 'explanation', '') return dict(status=status, message=message)
该方法允许应用程序显示某些 HTTP 错误(404 等)的自定义页面。
三、路由策略
Pecan 使用称为对象分发(object-dispatch)的路由策略(routing strategy)将 HTTP 请求映射到控制器,然后将方法回调。对象分发首先将路径拆分为组件列表,然后从根控制器开始遍历对象路径。您可以将应用程序的控制器想象成一棵对象树(对象树的分支直接映射到 URL 路径)。
网上有个比较典型的小例子:
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()
路由路径:
└── / ├── /hours └── /catalog └── /catalog/books └── /catalog/books/bestsellers
请求示例:
-
http://127.0.0.1:8080/catalog/
:
![[Pasted image 20230713103658.png|200]] -
http://127.0.0.1:8080/catalog/books/bestsellers
:
![[iShot_2023-07-13_10.33.43.png|200]]
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端