调用子请求

1.4 新版功能.

Pyramid 允许您在处理请求期间的任意点调用子请求。调用子请求允许您获取 response 从视图中调用的对象 Pyramid 在同一应用程序中执行可调用的不同视图时的应用程序。

下面是一个使用子请求的示例应用程序:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.request import Request

def view_one(request):
    subreq = Request.blank('/view_two')
    response = request.invoke_subrequest(subreq)
    return response

def view_two(request):
    request.response.body = 'This came from view_two'
    return request.response

if __name__ == '__main__':
    config = Configurator()
    config.add_route('one', '/view_one')
    config.add_route('two', '/view_two')
    config.add_view(view_one, route_name='one')
    config.add_view(view_two, route_name='two')
    app = config.make_wsgi_app()
    server = make_server('0.0.0.0', 8080, app)
    server.serve_forever()

什么时候? /view_one 在浏览器中查看,在浏览器窗格中打印的文本将 This came from view_two . 这个 view_one 视图使用 pyramid.request.Request.invoke_subrequest() 从另一个视图获取响应的API (view_two )在同一应用程序中执行时。它是通过构造一个新的请求来实现的,该请求有一个URL,它知道该URL将与 view_two 查看注册,并将新请求传递给 pyramid.request.Request.invoke_subrequest() . 这个 view_two 调用了View Callable,它返回了一个响应。这个 view_one 然后只返回从 view_two 查看可调用。

注意,如果通过子请求调用的视图可调用实际上返回 字面意义的 响应对象。任何使用渲染器或返回对象的可调用视图,当通过 pyramid.request.Request.invoke_subrequest() 将返回响应对象:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.request import Request

def view_one(request):
    subreq = Request.blank('/view_two')
    response = request.invoke_subrequest(subreq)
    return response

def view_two(request):
    return 'This came from view_two'

if __name__ == '__main__':
    config = Configurator()
    config.add_route('one', '/view_one')
    config.add_route('two', '/view_two')
    config.add_view(view_one, route_name='one')
    config.add_view(view_two, route_name='two', renderer='string')
    app = config.make_wsgi_app()
    server = make_server('0.0.0.0', 8080, app)
    server.serve_forever()

即使 view_two View Callable返回了一个字符串,它的调用方式是 string 与找到的视图注册相关联的渲染器将其转换为“真实”响应对象,供使用 view_one .

能够通过间接调用视图来无条件地获取响应对象是使用 pyramid.request.Request.invoke_subrequest() 而不是简单地导入一个可调用的视图并直接执行它。注意,如果您 can 直接调用视图。如果您真的只需要一个恰好是视图可调用的函数返回的文本信息,那么子请求会更慢,也更不方便。

注意,默认情况下,如果子请求调用的可调用视图引发异常,则异常将引发到的调用方 invoke_subrequest() 即使你有一个 exception view 配置:

 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
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.request import Request

def view_one(request):
    subreq = Request.blank('/view_two')
    response = request.invoke_subrequest(subreq)
    return response

def view_two(request):
    raise ValueError('foo')

def excview(request):
    request.response.body = b'An exception was raised'
    request.response.status_int = 500
    return request.response

if __name__ == '__main__':
    config = Configurator()
    config.add_route('one', '/view_one')
    config.add_route('two', '/view_two')
    config.add_view(view_one, route_name='one')
    config.add_view(view_two, route_name='two', renderer='string')
    config.add_view(excview, context=Exception)
    app = config.make_wsgi_app()
    server = make_server('0.0.0.0', 8080, app)
    server.serve_forever()

当我们运行上述代码并访问 /view_one 在浏览器中, excview exception viewnot 被处决。相反,呼叫 invoke_subrequest() 将导致 ValueError 将引发异常,将永远不会生成响应。我们可以改变这种行为;下面我们将在讨论 use_tweens 争论。

带tweens的子请求

这个 pyramid.request.Request.invoke_subrequest() API接受两个参数:必需的位置参数 request 和可选的关键字参数 use_tweens 默认为 False .

这个 request 传递给API的对象必须是实现金字塔请求接口的对象(例如 pyramid.request.Request 实例)。如果 use_tweensTrue ,请求将发送到 tween 在最接近请求入口的二层间堆栈中。如果 use_tweensFalse ,请求将发送到主路由器处理程序,并且不会调用中间层。

在上面的示例中,调用 invoke_subrequest() 总是会引发异常。这是因为它使用的是 use_tweens ,这就是 False . 或者,你可以通过 use_tweens=True 确保它将异常转换为响应,如果 exception view 配置,而不是引发异常。这是因为异常视图由异常视图调用 tween 如上所述 自定义异常视图 当任何视图引发异常时。

我们可以通过 use_tweens=True 呼唤 invoke_subrequest() ,像这样:

 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
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.request import Request

def view_one(request):
    subreq = Request.blank('/view_two')
    response = request.invoke_subrequest(subreq, use_tweens=True)
    return response

def view_two(request):
    raise ValueError('foo')

def excview(request):
    request.response.body = b'An exception was raised'
    request.response.status_int = 500
    return request.response

if __name__ == '__main__':
    config = Configurator()
    config.add_route('one', '/view_one')
    config.add_route('two', '/view_two')
    config.add_view(view_one, route_name='one')
    config.add_view(view_two, route_name='two', renderer='string')
    config.add_view(excview, context=Exception)
    app = config.make_wsgi_app()
    server = make_server('0.0.0.0', 8080, app)
    server.serve_forever()

在上述情况下,调用 request.invoke_subrequest(subreq) 不会引发异常。相反,它将从尝试调用的 view_two ,因为调用异常视图以生成响应的tween正在运行,因此 excview 执行。

这是指定 use_tweens=Trueuse_tweens=False 论据 invoke_subrequest() . use_tweens=True 也可能意味着调用事务提交或中止子请求中执行的逻辑(如果 pyramid_tm 在tween列表中,如果 pyramid_debugtoolbar 在吐温列表中,以及其他吐温相关的副作用,由您的特定吐温列表定义。

这个 invoke_subrequest() 函数也无条件地执行以下操作:

  • 它管理线程本地堆栈,以便 get_current_request()get_current_registry() 在请求期间工作(他们将返回子请求而不是原始请求)。
  • 它增加了一个 registry 属性和 invoke_subrequest 属性(一个可调用的)到它被传递到的请求对象。
  • 它设置请求扩展(例如通过 add_request_method() )在作为 request .
  • 它引起了 NewRequest 在请求处理开始时发送的事件。
  • 它引起了 ContextFound 找到上下文资源时要发送的事件。
  • 它确保传入请求所隐含的用户具有调用视图可调用视图之前调用该视图的必要授权。
  • 它调用任何 response callback 如果从金字塔应用程序获得响应,则在子请求的生存期内定义的函数。
  • 它引起了 NewResponse 获取响应时要发送的事件。
  • 它调用任何 finished callback 在子请求的生存期内定义的函数。

子请求的调用或多或少与 Pyramid 从Web客户端的路由器 use_tweens=True .什么时候? use_tweens=False 粗花呢被跳过,但其他步骤都会发生。

用原版是个糟糕的主意 request 对象作为参数 invoke_subrequest() . 您应该构建一个新的请求,而不是像上面的示例中所演示的那样,使用 pyramid.request.Request.blank() . 一旦构建了一个请求对象,就需要对其进行按摩,以匹配希望在子请求期间执行的视图可调用性。这可以通过调整子请求的URL、头、请求方法和其他属性来实现。文件 pyramid.request.Request 显示应调用的方法和应根据创建的请求设置的属性,然后将其按摩到与要通过子请求调用的视图实际匹配的对象中。

我们已经演示了在可调用视图中使用子请求,但是您可以使用 invoke_subrequest() 来自tween或事件处理程序的API。尽管你能做到,但调用 invoke_subrequest() 在tween中,因为tween已经可以访问一个将导致子请求的函数(它们通过 handle 函数)。可以调用 invoke_subrequest() 但是,从事件处理程序中。

调用异常视图

1.7 新版功能.

Pyramid 应用程序可能定义 exception views 它可以处理在处理请求时从代码中逸出的任何引发的异常。默认情况下,未处理的异常将由 EXCVIEW tween 然后,它将查找可以处理异常类型的异常视图,并生成适当的错误响应。

Pyramid 1.7 pyramid.request.Request.invoke_exception_view() 它允许用户在手动处理异常时调用异常视图。在一些不同的情况下,这是有用的:

  • 手动处理丢失当前调用堆栈或流的异常。
  • 处理上下文之外的异常 EXCVIEW 吐温。tween仅覆盖请求处理管道的某些部分(请参见 请求处理 )还有一些角落的情况下,可以引发一个异常,它仍然会冒泡到中间件,可能到Web服务器,在这种情况下,一个通用的 500 Internal Server Error 将返回给客户。

以下是使用 pyramid.request.Request.invoke_exception_view()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
def foo(request):
    try:
        some_func_that_errors()
        return response
    except Exception:
        response = request.invoke_exception_view()
        if response is not None:
            return response
        else:
            # there is no exception view for this exception, simply
            # re-raise and let someone else handle it
            raise

请注意,在大多数情况下,您不需要编写这样的代码,您可以依赖 EXCVIEW 特温帮你处理这个。