中间件¶
中间件组件提供了一种在框架路由每个请求之前、在路由每个请求之后但在调用目标响应程序之前或在为每个请求返回响应之前执行逻辑的方法。
注解
与钩子不同,中间件方法全局应用于整个应用程序。
组件注册到 middleware Kwarg在实例化Falcon's时 App class . 中间件组件只是一个实现下面定义的一个或多个事件处理程序方法的类。
Falcon的中间件接口定义如下:
class ExampleMiddleware:
def process_request(self, req, resp):
"""Process the request before routing it.
Note:
Because Falcon routes each request based on req.path, a
request can be effectively re-routed by setting that
attribute to a new value from within process_request().
Args:
req: Request object that will eventually be
routed to an on_* responder method.
resp: Response object that will be routed to
the on_* responder.
"""
def process_resource(self, req, resp, resource, params):
"""Process the request after routing.
Note:
This method is only called when the request matches
a route to a resource.
Args:
req: Request object that will be passed to the
routed responder.
resp: Response object that will be passed to the
responder.
resource: Resource object to which the request was
routed.
params: A dict-like object representing any additional
params derived from the route's URI template fields,
that will be passed to the resource's responder
method as keyword arguments.
"""
def process_response(self, req, resp, resource, req_succeeded):
"""Post-processing of the response (after routing).
Args:
req: Request object.
resp: Response object.
resource: Resource object to which the request was
routed. May be None if no route was found
for the request.
req_succeeded: True if no exceptions were raised while
the framework processed and routed the request;
otherwise False.
"""
# A middleware instance can then be added to the Falcon app init
from falcon import App
app = App(middleware=[ExampleMiddleware()])
ASGI中间件接口类似于WSGI,但也支持标准的ASGI生命周期事件。但是,由于生命周期事件是ASGI规范的一个可选部分,它们可能会触发,也可能不会触发,这取决于您的ASGI服务器。
class ExampleMiddleware:
async def process_startup(self, scope, event):
"""Process the ASGI lifespan startup event.
Invoked when the server is ready to start up and
receive connections, but before it has started to
do so.
To halt startup processing and signal to the server that it
should terminate, simply raise an exception and the
framework will convert it to a "lifespan.startup.failed"
event for the server.
Args:
scope (dict): The ASGI scope dictionary for the
lifespan protocol. The lifespan scope exists
for the duration of the event loop.
event (dict): The ASGI event dictionary for the
startup event.
"""
async def process_shutdown(self, scope, event):
"""Process the ASGI lifespan shutdown event.
Invoked when the server has stopped accepting
connections and closed all active connections.
To halt shutdown processing and signal to the server
that it should immediately terminate, simply raise an
exception and the framework will convert it to a
"lifespan.shutdown.failed" event for the server.
Args:
scope (dict): The ASGI scope dictionary for the
lifespan protocol. The lifespan scope exists
for the duration of the event loop.
event (dict): The ASGI event dictionary for the
shutdown event.
"""
async def process_request(self, req, resp):
"""Process the request before routing it.
Note:
Because Falcon routes each request based on req.path, a
request can be effectively re-routed by setting that
attribute to a new value from within process_request().
Args:
req: Request object that will eventually be
routed to an on_* responder method.
resp: Response object that will be routed to
the on_* responder.
"""
async def process_resource(self, req, resp, resource, params):
"""Process the request after routing.
Note:
This method is only called when the request matches
a route to a resource.
Args:
req: Request object that will be passed to the
routed responder.
resp: Response object that will be passed to the
responder.
resource: Resource object to which the request was
routed.
params: A dict-like object representing any additional
params derived from the route's URI template fields,
that will be passed to the resource's responder
method as keyword arguments.
"""
async def process_response(self, req, resp, resource, req_succeeded):
"""Post-processing of the response (after routing).
Args:
req: Request object.
resp: Response object.
resource: Resource object to which the request was
routed. May be None if no route was found
for the request.
req_succeeded: True if no exceptions were raised while
the framework processed and routed the request;
otherwise False.
"""
async def process_request_ws(self, req, ws):
"""Process a WebSocket handshake request before routing it.
Note:
Because Falcon routes each request based on req.path, a
request can be effectively re-routed by setting that
attribute to a new value from within process_request().
Args:
req: Request object that will eventually be
passed into an on_websocket() responder method.
ws: The WebSocket object that will be passed into
on_websocket() after routing.
"""
async def process_resource_ws(self, req, ws, resource, params):
"""Process a WebSocket handshake request after routing.
Note:
This method is only called when the request matches
a route to a resource.
Args:
req: Request object that will be passed to the
routed responder.
ws: WebSocket object that will be passed to the
routed responder.
resource: Resource object to which the request was
routed.
params: A dict-like object representing any additional
params derived from the route's URI template fields,
that will be passed to the resource's responder
method as keyword arguments.
"""
# A middleware instance can then be added to the Falcon app init
from falcon.asgi import App
app = App(middleware=[ExampleMiddleware()])
还可以实现与ASGI和WSGI应用程序兼容的中间件组件。这是通过应用 *_async 后缀,以区分每个中间件方法的两个不同版本,如下例所示:
class ExampleMiddleware:
def process_request(self, req, resp):
"""Process WSGI request using synchronous logic.
Note that req and resp are instances of falcon.Request and
falcon.Response, respectively.
"""
async def process_request_async(self, req, resp):
"""Process ASGI request using asynchronous logic.
Note that req and resp are instances of falcon.asgi.Request and
falcon.asgi.Response, respectively.
"""
小技巧
因为 process_request 如果组件修改,则在路由发生之前执行 req.path
在其 process_request 方法,框架将使用修改后的值来路由请求。
例如::
# Route requests based on the host header.
req.path = '/' + req.host + req.path
小技巧
这个 process_resource 仅当请求与资源的路由匹配时才调用方法。要在找不到路由时执行操作,请执行以下操作: sink
可以代替。
小技巧
为了将数据从中间件函数传递到资源函数,请使用 req.context
和 resp.context
物体。这些上下文对象用于在应用程序通过框架时保存特定于应用程序的请求和响应数据。
每个组件的 process_request , process_resource 和 process_response 方法按照通过 middleware 克沃格 falcon.App
或 falcon.asgi.App
.例如,如果中间件对象列表作为 [mob1, mob2, mob3]
执行顺序如下:
mob1.process_request
mob2.process_request
mob3.process_request
mob1.process_resource
mob2.process_resource
mob3.process_resource
<route to resource responder method>
mob3.process_response
mob2.process_response
mob1.process_response
注意,每个组件不需要实现所有 process_* 方法;如果缺少三个方法中的一个,则将其视为堆栈中的noop。例如,如果 mob2
未实现 process_request 和 mob3
未实现 process_response 执行顺序如下:
mob1.process_request
_
mob3.process_request
mob1.process_resource
mob2.process_resource
mob3.process_resource
<route to responder method>
_
mob2.process_response
mob1.process_response
短路¶
A process_request 或 process_resource 中间件方法可以通过设置来短路进一步的请求处理 falcon.Response.complete
到 True
,例如:
resp.complete = True
方法返回后,设置此标志将导致框架跳过任何剩余的 process_request 和 process_resource 方法,以及将请求路由到的响应程序方法。但是,任何 process_response 中间件方法仍将被调用。
以类似的方式,设置 falcon.Response.complete
到 True
从内部 process_resource 方法将在该点短路进一步请求处理。
在下面的例子中你会看到如何处理短路 falcon.Response.complete
已设置为 True
也就是说,框架将阻止 mob3.process_request
,所有 process_resource 方法,以及处理请求的路由响应程序方法。然而,所有 process_response 仍将调用方法::
mob1.process_request
mob2.process_request # resp.complete = True
<skip mob3.process_request>
<skip mob1/mob2/mob3.process_resource>
<skip route to resource responder method>
mob3.process_response
mob2.process_response
mob1.process_response
这个特性提供了可以预先构造响应的用例,例如在缓存的情况下。
异常处理¶
如果其中一个 process_request 中间件方法引发异常,将根据异常类型进行处理。如果类型与注册的错误处理程序匹配,则将调用该处理程序,然后框架将开始展开堆栈,跳过任何较低层。错误处理程序本身可能引发的实例 HTTPError
或 HTTPStatus
,在这种情况下,框架将使用后一个异常更新 resp 对象。
注解
默认情况下,框架安装两个处理程序,一个用于 HTTPError
一个 HTTPStatus
.这些可以通过 add_error_handler()
.
无论如何,框架将继续展开中间件堆栈。例如,如果 mob2.process_request 若要引发错误,框架将按以下方式执行堆栈:
mob1.process_request
mob2.process_request
<skip mob1/mob2 process_resource>
<skip mob3.process_request>
<skip mob3.process_resource>
<skip route to resource responder method>
mob3.process_response
mob2.process_response
mob1.process_response
如上所示,默认情况下,所有 process_response 方法将被执行,即使在 process_request , process_resource 或 *on_* *资源响应程序引发错误。此行为由 App class's independent_middleware 关键字参数。
最后,如果 process_response 方法引发错误或路由 on_*
响应程序方法本身引发了一个错误,异常将以与上面类似的方式处理。然后,框架将执行堆栈上剩余的任何中间件。