路由¶
Falcon使用基于资源的路由来鼓励REST风格的架构。每个资源都由一个类表示,该类负责处理资源支持的所有HTTP方法。
对于资源支持的每个HTTP方法,该类都会实现相应的Python方法,其名称以 on_
并且以小写的HTTP方法名结束(例如, on_get()
, on_patch()
, on_delete()
等)
备注
Falcon中的资源由配置路由时在应用程序启动时创建的单个类实例表示。这最大限度地减少了路由开销并简化了资源类的实现。对于WSGI应用程序,这也意味着资源类必须以线程安全的方式实现(另请参阅: Falcon线安全吗? )。
Falcon路由传入请求(包括 WebSocket handshakes )到基于一组URI模板的资源。如果客户端请求的路径与给定路由的模板匹配,则会将请求传递到关联的资源进行处理。
下面是一个简单的例子,展示了所有部件是如何组合在一起的:
import json
import falcon
class ImagesResource:
def on_get(self, req, resp):
doc = {
'images': [
{
'href': '/images/1eaf6ef1-7f2d-4ecc-a8d5-6e8adba7cc0e.png'
}
]
}
# Create a JSON representation of the resource; this could
# also be done automatically by assigning to resp.media
resp.text = json.dumps(doc, ensure_ascii=False)
# The following line can be omitted because 200 is the default
# status returned by the framework, but it is included here to
# illustrate how this may be overridden as needed.
resp.status = falcon.HTTP_200
app = falcon.App()
images = ImagesResource()
app.add_route('/images', images)
import json
import falcon
import falcon.asgi
class ImagesResource:
async def on_get(self, req, resp):
doc = {
'images': [
{
'href': '/images/1eaf6ef1-7f2d-4ecc-a8d5-6e8adba7cc0e.png'
}
]
}
# Create a JSON representation of the resource; this could
# also be done automatically by assigning to resp.media
resp.text = json.dumps(doc, ensure_ascii=False)
# The following line can be omitted because 200 is the default
# status returned by the framework, but it is included here to
# illustrate how this may be overridden as needed.
resp.status = falcon.HTTP_200
app = falcon.asgi.App()
images = ImagesResource()
app.add_route('/images', images)
如果没有与请求匹配的路由,则控制传递给默认响应器,该响应器只是引发 HTTPRouteNotFound
。默认情况下,对于常规HTTP请求,此错误将呈现为404响应,对于 WebSocket 握手。可以通过添加自定义错误处理程序来修改此行为(另请参阅 this FAQ topic )。
另一方面,如果路由匹配,但资源没有为请求的HTTP方法实现响应器,则框架将调用默认响应器,该响应器引发 HTTPMethodNotAllowed
。默认情况下,此类将呈现为常规HTTP请求的405响应和 WebSocket 握手。
Falcon还为OPTIONS请求提供默认响应器,该响应器会考虑为目标资源实现哪些方法。
默认行为¶
Falcon的默认路由引擎基于一个决策树,该树首先被编译成Python代码,然后由运行时评估。默认情况下,只有当路由器处理第一个请求时才会编译决策树。看到了吗 CompiledRouter
了解更多详细信息。
这个 falcon.App.add_route()
和 falcon.asgi.App.add_route()
方法用于将URI模板与资源相关联。然后Falcon根据这些模板将传入的请求映射到资源。
Falcon的默认路由器使用python类来表示资源。实际上,这些类在应用程序中充当控制器。它们将一个传入的请求转换为一个或多个内部操作,然后根据这些操作的结果组合一个对客户机的响应。(另见: Tutorial: Creating Resources )
┌────────────┐
request → │ │
│ Resource │ ↻ Orchestrate the requested action
│ Controller │ ↻ Compose the result
response ← │ │
└────────────┘
每个资源类定义不同的“响应者”方法,资源允许的每个HTTP方法对应一个。响应程序名称以开头 on_
并根据它们处理的HTTP方法命名,如 on_get()
, on_post()
, on_put()
等。
备注
如果您的资源不支持特定的HTTP方法,只需省略相应的响应程序,falcon将使用默认的响应程序,该响应程序将引发 HTTPMethodNotAllowed
当请求该方法时。通常,这会导致向客户机发送405响应。
响应程序必须始终定义至少两个要接收的参数 Request
和 Response
对象,分别为:
def on_post(self, req, resp):
pass
对于ASGI应用程序,响应程序必须是协同程序函数:
async def on_post(self, req, resp):
pass
这个 Request
对象表示传入的HTTP请求。它公开用于检查头、查询字符串参数和与请求关联的其他元数据的属性和方法。还提供了类似于流对象的文件,用于读取请求主体中包含的任何数据。
这个 Response
对象表示应用程序对上述请求的HTTP响应。它提供设置状态、标题和正文数据的属性和方法。这个 Response
对象还公开类似dict的内容 context
属性,用于将任意数据传递给挂钩和中间件方法。
备注
而不是直接操纵 Response
对象,响应程序可以引发 HTTPError
或 HTTPStatus
. Falcon将把这些异常转换为适当的HTTP响应。或者,您可以通过 add_error_handler()
.
除了标准 req 和 resp 参数,如果路由的模板包含字段表达式,则任何希望接收该路由请求的响应程序都必须接受以模板中定义的相应字段名命名的参数。
字段表达式由带括号的字段名组成。例如,给定以下模板:
/user/{name}
对的卖出请求 '/user/kgriffs'
会导致框架调用 on_put()
路由资源类上的响应程序方法,传递 'kgriffs'
通过额外的 name 响应程序定义的参数:
# Template fields correspond to named arguments or keyword
# arguments, following the usual req and resp args.
def on_put(self, req, resp, name):
pass
# Template fields correspond to named arguments or keyword
# arguments, following the usual req and resp args.
async def on_put(self, req, resp, name):
pass
因为字段名对应于响应程序方法中的参数名,所以它们必须是有效的Python标识符。
单个路径段可以包含一个或多个字段表达式,字段不需要跨越整个路径段。例如::
/repos/{org}/{repo}/compare/{usr0}:{branch0}...{usr1}:{branch1}
/serviceRoot/People('{name}')
(另请参见 Falcon tutorial 有关在示例应用程序上下文中设置路由的其他示例和演练。)
场转换器¶
Falcon的默认路由器支持使用字段转换器来转换URI模板字段值。现场转换器也可以执行简单的输入验证。例如,以下URI模板使用 int 转换值的转换器 tid 给 Python int
,但仅当它正好有八位数字时::
/teams/{tid:int(8)}
如果该值格式错误且无法转换,Falcon将拒绝请求,并对客户端作出404响应。
转换器用字段表达式中给出的参数规范进行实例化。这些规范遵循标准的Python语法来传递参数。例如,下面代码中的注释显示了在URI模板中给定不同参数规范的情况下如何实例化转换器:
# IntConverter()
app.add_route(
'/a/{some_field:int}',
some_resource
)
# IntConverter(8)
app.add_route(
'/b/{some_field:int(8)}',
some_resource
)
# IntConverter(8, min=10000000)
app.add_route(
'/c/{some_field:int(8, min=10000000)}',
some_resource
)
(另请参阅如何 UUIDConverter
在Falcon的ASGI教程中使用: 图像资源 。)
内置转换器¶
标识符 |
等级 |
例子 |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- class falcon.routing.FloatConverter(min: Optional[float] = None, max: Optional[float] = None, finite: bool = True)[源代码]¶
将字段值转换为浮点型。
标识符: float
- 关键字参数:
- class falcon.routing.UUIDConverter[源代码]¶
将字段值转换为uuid.uuid。
标识符: uuid
为了进行转换,字段值必须由32个十六进制数字组成的字符串,如中所定义。 RFC 4122, Section 3. 但是,请注意,连字符和urn前缀是可选的。
- class falcon.routing.DateTimeConverter(format_string='%Y-%m-%dT%H:%M:%SZ')[源代码]¶
将字段值转换为日期时间。
标识符: dt
- 关键字参数:
format_string (str) -- 用于将字段值解析为日期时间的字符串。支持strptime()识别的任何格式(默认
'%Y-%m-%dT%H:%M:%SZ'
)
- class falcon.routing.PathConverter[源代码]¶
用于匹配路径其余部分的已转换字段。
此字段转换器匹配URL路径的其余部分,将其作为字符串返回。
当前仅当在URL模板的末尾使用时才支持此转换器。
Falcon的经典路由规则也适用于此转换器:考虑模板
'/foo/bar/{matched_path:path}'
,这条路'/foo/bar'
将要 not 匹配路线;'/foo/bar/'
将会匹配,制作matched_path=''
,何时strip_url_path_trailing_slash
是False
(默认设置),虽然它将 not 当该选项为True
。(另请参阅: Falcon如何处理请求路径中的尾随斜杠? )
- CONSUME_MULTIPLE_SEGMENTS = True¶
当设置为时
True
它表明该转换器将使用多个URL路径段。目前使用的转换器具有CONSUME_MULTIPLE_SEGMENTS=True
必须位于URL模板的末尾,这意味着它将消耗所有剩余的URL路径段。
自定义转换器¶
自定义转换器可以通过 converters
路由器选项。转换器只是一个实现 BaseConverter
接口:
自定义路由器¶
在实例化时可以指定自定义路由引擎 falcon.App()
或 falcon.asgi.App()
. 例如:
router = MyRouter()
app = App(router=router)
自定义路由器可以从默认值派生 CompiledRouter
引擎,或实现完全不同的路由策略(如基于对象的路由)。
自定义路由器是实现以下接口的任何类:
class MyRouter:
def add_route(self, uri_template, resource, **kwargs):
"""Adds a route between URI path template and resource.
Args:
uri_template (str): A URI template to use for the route
resource (object): The resource instance to associate with
the URI template.
Keyword Args:
suffix (str): Optional responder name suffix for this
route. If a suffix is provided, Falcon will map GET
requests to ``on_get_{suffix}()``, POST requests to
``on_post_{suffix}()``, etc. In this way, multiple
closely-related routes can be mapped to the same
resource. For example, a single resource class can
use suffixed responders to distinguish requests for
a single item vs. a collection of those same items.
Another class might use a suffixed responder to handle
a shortlink route in addition to the regular route for
the resource.
**kwargs (dict): Accepts any additional keyword arguments
that were originally passed to the falcon.App.add_route()
method. These arguments MUST be accepted via the
double-star variadic pattern (**kwargs), and ignore any
unrecognized or unsupported arguments.
"""
def find(self, uri, req=None):
"""Search for a route that matches the given partial URI.
Args:
uri(str): The requested path to route.
Keyword Args:
req(Request): The Request object that will be passed to
the routed responder. The router may use `req` to
further differentiate the requested route. For
example, a header may be used to determine the
desired API version and route the request
accordingly.
Note:
The `req` keyword argument was added in version
1.2. To ensure backwards-compatibility, routers
that do not implement this argument are still
supported.
Returns:
tuple: A 4-member tuple composed of (resource, method_map,
params, uri_template), or ``None`` if no route matches
the requested path.
"""
带后缀的响应器¶
虽然Falcon鼓励REST架构风格,但它足够灵活,可以适应其他范例。考虑一下为计算器构建API的任务,该计算器可以加减两个数字。您可以实现以下内容:
class Add():
def on_get(self, req, resp):
resp.text = str(req.get_param_as_int('x') + req.get_param_as_int('y'))
resp.status = falcon.HTTP_200
class Subtract():
def on_get(self, req, resp):
resp.text = str(req.get_param_as_int('x') - req.get_param_as_int('y'))
resp.status = falcon.HTTP_200
add = Add()
subtract = Subtract()
app = falcon.App()
app.add_route('/add', add)
app.add_route('/subtract', subtract)
但是,这种方法突出了这样一种情况,即按资源分组对于您的域可能没有意义。在此上下文中,加法和减法似乎在概念上没有映射到两个独立的资源集合。我们可能希望根据它们的函数属性(即,接受两个数字,对它们做一些操作,然后返回结果)来对它们进行分组,而不是基于从每个数据中“获取”不同资源的想法来将它们分开。
有了带后缀的Responders,我们就可以做到这一点,以更过程化的风格重写上面的示例:
class Calculator():
def on_get_add(self, req, resp):
resp.text = str(req.get_param_as_int('x') + req.get_param_as_int('y'))
resp.status = falcon.HTTP_200
def on_get_subtract(self, req, resp):
resp.text = str(req.get_param_as_int('x') - req.get_param_as_int('y'))
resp.status = falcon.HTTP_200
calc = Calculator()
app = falcon.App()
app.add_route('/add', calc, suffix='add')
app.add_route('/subtract', calc, suffix='subtract')
在第二次迭代中,使用带后缀的Responder,我们能够根据响应者的操作而不是他们表示的数据对响应者进行分组。这为我们提供了额外的灵活性,以适应纯粹的RESTful方法根本不适合的情况。
默认路由器¶
- class falcon.routing.CompiledRouter[源代码]¶
将其路由逻辑编译为python代码的快速uri路由器。
一般情况下,您不需要直接使用该路由器类,因为在初始化Falcon.App类时,默认情况下会创建一个实例。
路由器将URI路径视为一个URI段树,并通过一次检查一个URI段进行搜索。它不会为每次查找解释路由树,而是生成内联的定制python代码来执行搜索,然后编译该代码。这使得路由处理相当快。
编译过程延迟到第一次使用路由器(针对第一个路由的请求),以减少启动应用程序所需的时间。当添加了大量路由时,这可能会显著延迟应用程序的第一次响应。将最后一条路由添加到应用程序时 compile 可以提供标志以强制路由器立即编译,从而避免第一次响应的任何延迟。
备注
使用多线程Web服务器托管应用程序时,可能会在启动时同时路由多个请求。因此,该框架使用锁来确保只执行决策树的一次编译。
另请参阅
CompiledRouter.add_route()
- add_route(uri_template, resource, **kwargs)[源代码]¶
在URI路径模板和资源之间添加路由。
可以重写此方法以自定义添加路由的方式。
- 参数:
- 关键字参数:
suffix (str) -- 此路由的可选响应程序名称后缀。如果提供了后缀,falcon将把get请求映射到
on_get_{{suffix}}()
,将请求发布到on_post_{{suffix}}()
等等。通过这种方式,可以将多个密切相关的路由映射到同一个资源。例如,单个资源类可以使用后缀响应器来区分单个项目的请求与那些相同项目的集合。另一个类可能使用后缀响应器来处理短链接路由以及资源的常规路由。compile (bool) -- 可用于编译此呼叫的路由逻辑的可选标志。默认情况下,
CompiledRouter
延迟编译,直到路由第一个请求。这可能会在处理第一个请求时引入明显的延迟,特别是当应用程序实现大量路由时。设置 compile 至True
添加最后一个路由时,可确保第一个请求在这种情况下不会延迟(默认为False
)。。。注意::始终将此标志设置为True
当一次添加数百条新路由时,可能会减慢添加新路由的速度。建议仅将此标志设置为True
在添加最终路线时。
- find(uri, req=None)[源代码]¶
搜索与给定的部分URI匹配的路由。
- 参数:
uri (str) -- 请求的路由路径。
- 关键字参数:
req -- 这个
falcon.Request
或falcon.asgi.Request
对象,该对象将传递给路由的响应方。当前,此参数的值被忽略CompiledRouter
。路由完全基于路径。- 返回:
由(resource,method_map,params,uri_template)或
None
如果没有与请求的路径匹配的路由。- 返回类型:
- map_http_methods(resource, **kwargs)[源代码]¶
将HTTP方法(例如get、post)映射到资源对象的方法。
此方法是从
add_route()
可以重写以提供自定义映射策略。- 参数:
resource (instance) -- 表示休息资源的对象。默认映射HTTP方法
GET
到on_get()
,POST
到on_post()
等等。如果您的资源不支持任何HTTP方法,只需不定义相应的请求处理程序,Falcon就会做正确的事情。- 关键字参数:
suffix (str) -- 此路由的可选响应程序名称后缀。如果提供了后缀,falcon将把get请求映射到
on_get_{{suffix}}()
,将请求发布到on_post_{{suffix}}()
等等。通过这种方式,可以将多个密切相关的路由映射到同一个资源。例如,单个资源类可以使用后缀响应器来区分单个项目的请求与那些相同项目的集合。另一个类可能使用后缀响应器来处理短链接路由以及资源的常规路由。
路由实用程序¶
这个 falcon.routing 模块包含以下可由自定义路由引擎使用的实用程序。
- falcon.routing.set_default_responders(method_map, asgi=False)[源代码]¶
将资源上未显式定义的HTTP方法映射到默认响应器。
- 参数:
method_map -- 将HTTP方法映射到资源中显式定义的响应程序的dict。
asgi (bool) --
True
如果使用ASGI应用程序,False
否则(默认False
)。
- falcon.routing.compile_uri_template(template)[源代码]¶
将给定的URI模板字符串编译为模式匹配器。
此函数可用于构造自定义路由引擎,该引擎迭代可能的路由列表,尝试根据每个路由的已编译正则表达式匹配传入请求。
每个字段都转换为一个命名组,这样当找到匹配项时,可以使用
re.MatchObject.groupdict()
.此函数不支持默认路由器中使用的更灵活的模板语法。只能识别带括号字段表达式的简单路径。例如::
/ /books /books/{isbn} /books/{isbn}/characters /books/{isbn}/characters/{name}
警告
如果模板包含尾随斜杠字符,则会将其删除。
请注意,这是 不同 从… the default behavior 的
add_route()
与默认设置一起使用CompiledRouter
。这个
strip_url_path_trailing_slash
未考虑请求选项compile_uri_template()
。自 3.1 版本弃用.
- falcon.app_helpers.prepare_middleware(middleware: Iterable, independent_middleware: bool = False, asgi: bool = False) Tuple[tuple, tuple, tuple] [源代码]¶
检查中间件接口并准备请求处理的方法。
备注
此方法仅适用于WSGI应用程序。
- falcon.app_helpers.prepare_middleware_ws(middleware: Iterable) Tuple[list, list] [源代码]¶
检查中间件接口并为请求处理准备WebSocket方法。
备注
此方法仅适用于ASGI应用程序。
- 参数:
middleware (iterable) -- 中间件对象的可迭代。
- 返回:
A两件装的
(request_mw, resource_mw)
元组,其中 request_mw 是一个有序的列表,该列表包含process_request_ws()
方法,以及 resource_mw 是一个有序的列表,该列表包含process_resource_ws()
方法。- 返回类型:
自定义HTTP方法¶
虽然通常不建议这样做,但除了GET和PUT等标准HTTP方法之外,有些应用程序可能还需要支持非标准HTTP方法。要支持自定义HTTP方法,请使用以下方法之一:
理想情况下,如果在应用程序中不使用钩子,则可以通过重写
falcon.constants.COMBINED_METHODS
. 例如::import falcon.constants falcon.constants.COMBINED_METHODS += ['FOO', 'BAR']
由于钩子的性质,如果确实使用钩子,则需要将falcon_custom_http_methods环境变量定义为自定义方法的逗号分隔列表。例如::
$ export FALCON_CUSTOM_HTTP_METHODS=FOO,BAR
一旦您使用了适当的方法,您的自定义方法应该是活动的。然后,您可以像任何其他HTTP方法一样定义请求方法:
# Handle the custom FOO method
def on_foo(self, req, resp):
pass
# Handle the custom FOO method
async def on_foo(self, req, resp):
pass