路由
Index-py 的路由基于 Radix Tree。
基本用法
使用装饰器
与 bottle/flask 之类的框架一样,Index-py 支持使用装饰器注册路由。下面的例子里,name 是路由名称,这在反向查找路由时会起到作用。
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
如果 name 没有被指定,则会默认使用被注册的可调用对象的 __name__ 属性。
如果指定路由的 name 为 None,则无法通过 name 查找到该路由。
路由对象
事实上,装饰器路由申明方式是如下方法的快捷方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
Index-py 的路由对象有两种,分别对应 Http 和 WebSocket 方法。
1 2 3 4 5 | |
-
path指定路由能匹配到的字符串 -
endpoint指定路由对应的可调用对象 -
name为路由指定名称,name为None时,此路由将没有名称;name为""时,将自动读取endpoint.__name__作为路由名称。
预处理
使用路由对象注册的可调用对象 endpoint,Index-py 会自动为其注册一个装饰器,用于处理部分参数的自动校验和注入。
中间件
你可以对路由对象使用装饰器,这将会作用到 endpoint 上,但与直接对 endpoint 使用装饰器不同的是它作用于 Index-py 预处理后的 endpoint 上。
你可以在注册的中间件里捕捉到可能抛出的参数校验异常。
在本文档里,这样注册的装饰器被称为中间件。“中间件”这一名称主要是为了沿用其他框架中的说法。
1 | |
像注册普通的装饰器一样,你可以注册多个;执行顺序也一样,由远到近的执行。
1 | |
并且,你同样可以在使用装饰器进行路由注册时注册中间件,如下所示,其执行顺序同样是由右到左。
1 2 | |
限定请求方法
指定支持 GET 方法时,HEAD 将被自动允许。
限定了请求方法后,OPTIONS 的请求将被自动处理。反之,你需要自行处理 OPTIONS 方法。
在使用装饰器注册时可以直接限定该路由能够接受的请求方法,目前仅支持以下五种 HTTP 方法的限定。如果你没有指定,则默认允许所有请求方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | |
如上代码是在内部使用了 required_method 装饰器来达到限定请求方法的目的,你也可以选择手动注册装饰器,这将能限定更多种类的请求。代码样例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
列表式注册
Index-py 同样支持类似于 Django 的列表式写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
路径参数
使用 {name:type} 可以标注路径参数,目前支持的类型有 str、int、decimal、date、uuid 和 path。
如果路径参数的类型为 str,可以忽略掉 :str,直接使用 {name}。
str 不能匹配到 /,如果需要匹配 / 请使用 path。
path 是极为特殊的参数类型,它只能出现在路径的最后,并且能匹配到所有的字符。
1 2 3 4 5 6 7 8 | |
反向查找
某些情况下,需要由路由名称反向生成对应的 URL 值,可以使用 app.router.url_for。
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
反向查找中,websocket 与 http 是互相独立的。通过 protocol 参数可以选择查找的路由,默认为 http。
路由分组
当需要把某一些路由归为一组时,可使用 Routes 对象。
Routes 对象拥有 .http 和 .websocket 方法允许你使用装饰器方式注册路由,使用方法与 app.router 相同。
Routes 也同样允许你使用类似于 Django 一样的路由申明方式,示例如下。
1 2 3 4 5 6 7 8 9 10 | |
使用 << 运算符即可注册 Routes 中所有路由给 app.router,并且这一运算的返回结果是 app.router,这意味着你可以进行链式调用。
1 2 3 4 | |
当然,你也可以直接在初始化 Index 对象时传入。
1 2 3 4 5 | |
路由组合
Routes 可以轻松和其他 Routes 组合起来。
1 2 3 | |
并且 << 的结果是运算左侧的 Routes 对象,这意味着你可以链式调用它,如下所示。
1 2 3 4 5 | |
你也可以合并两个 Routes 成为一个新的 Routes 对象,而不是将其中一个合并到另一个里。
1 2 3 4 5 | |
名称空间
你可以为 Routes 设置 namespace 参数,这将在 Routes 对象中包含的每个路由名称(如果有的话)前加上 namespace:,以此来避免不同名称空间内的路由名称冲突。
1 | |
在使用 app.router.url_for 时不要忘记加上路由所在的名称空间前缀。
中间件注册
通过 Routes 你可以为整组路由注册一个或多个中间件。以下为简单的样例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
当然,你同样可以使用装饰器来注册中间件,与上例的结果没有什么不同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
公共前缀
有时候某一组的路由我们希望放到同一个前缀下,如下两段代码的结果是相同的。
1 2 3 4 | |
1 2 3 4 | |
注意事项
在使用 routes = "prefix" // Routes(......) 之后再调用 @routes.http 等方法注册路由时,并不会给后续的路由自动加上 "prefix" 前缀。你应当在一个路由分组内所有路由注册完成之后,再进行 "prefix" // routes 运算。
路由拓展
通过构建路由对象的序列(Sequence[BaseRoute])可以编写自己喜爱的路由注册方式,在最终都会合并进 Radix Tree 里。
FileRoutes
1 | |
这也是 Index.py 此项目的命名来源之一。
FileRoutes 是一个特殊的路由序列,它允许你将某一个 module 下所有的 .py 文件一一对应到其相对路径相同的路由。
中间件定义
__init__.py 中名为 HTTPMiddleware 的对象将被作为 HTTP 中间件、SocketMiddleware 将被作为 WebSocket 中间件,并作用于同目录下所有的路由。
处理器定义
除了 __init__.py 文件以外的 .py 文件中,名为 HTTP 的对象(任何可调用对象均可,函数、类等)将被视为 HTTP 处理器,名为 Socket 的对象(任何可调用对象均可,函数、类等)将被视为 WebSocket 处理器。
路由名称
在文件中定义名称为 name 的字符串将作为该文件对应的路由名称。
FileRoutes 同样拥有 namespace 参数,并且拥有同样的作用。
映射规则
module/filename.py 文件将对应路由 /filename,module/dirname/filename.py 将对应 /dirname/filename,以此类推。
文件映射有一个特殊规则:module/**/index.py 将负责处理 /**/ 路径的内容。
你可以将文件名或文件夹名修改为 module/{name}.py 以此接受路径参数。
可以为 FileRoutes 设置 suffix 参数,给每个路由加上后缀,譬如 suffix=".php" 这将使路径看起来很像 PHP 😀。
MultimethodRoutes
1 | |
MultimethodRoutes 是一个特殊的路由序列,它允许你使用如下方式注册路由,在不显式使用类的情况下拆分同一个 PATH 下的不同方法到多个函数中。除此之外,均与 Routes 相同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |