取势 明道 优术

作者为 扶 凯 发表

PSGI Applications

在 PSGI spec 中定义了有关 PSGI 的应用相关的内容.

PSGI 应用是一个 Perl 代码块的引用. 它需要一个参数, 环境变量, 并返回一个包含着三个值的数组.

这三个值是 HTTP 的状态 status, 响应的 header, 和 body. 下面是一个简单的例子:

my $app = sub {
    my $env = shift;
    return [
        '200',
        [ 'Content-Type' => 'text/plain' ],
        [ "Hello World" ], # or IO::Handle-like object
    ];
};

PSGI 环境变量的哈希

PSGI 环境变量的哈希是一个有很多键的哈希引用. 它主要是这些 (headers, body, etc), 都是从 HTTP::Request 解析并放到这个哈希中来给方便访问.

中间件

中间件会取得 PSGI 的应用, 并运行和传送 PSGI 的环境变量的哈希给它. 它在运行 PSGI 的应用之前, 如果有需要, 它可能会修改环境变量. 并且在运行完应用之后, 它也可能修改响应的内容.

Plack::Middleware

中间件是围绕在 PSGI 的应用上来封装的. 应用可以被一个或者多个中间件包围封装, 这样可以创建一层又一层就象洋葱 onion. 事件驱动和回调让中间件有着不同寻常的结构.

所有的中间件都继承 Plack::Middleware. 它是一个非常小的模块. 这个中间件模块只有 2 个很短的子函数  (注意 call() 和 prepare_app()这二个函数是由使用者来完成):

译注: 现在其实只有一个 wrap 函数了. 其中 to_app 现在是在 Plack::Component 模块中实现. to_app 是用于返回代码块.

sub wrap {
    my($self, $app, @args) = @_;
    if (ref $self) {
        $self->{app} = $app;
    } else {
        $self = $self->new({ app => $app, @args });
    }
    return $self->to_app;
}
 
sub to_app {
    my $self = shift;
    $self->prepare_app;
    return sub { $self->call(@_) };
}

这些函数是怎么样在一起工作的? 这些中间件的洋葱结构决定了, 可以这样构造:

my $app = MyWebApp->new->to_app;
$app = Plack::Middleware::A->wrap($app);
$app = Plack::Middleware::B->wrap($app);
$app = Plack::Middleware::C->wrap($app);

象下面这样写, 可能更加清楚和容易让你明白

my $app0 = MyWebApp->new->to_app;           # $app0->($env) 运行 web app
$app1 = Plack::Middleware::A->wrap($app0);  # $app1->($env) 调用 P::M::A->call() 这是 $app0->($env)
$app2 = Plack::Middleware::B->wrap($app1);  # $app2->($env) 调用 P::M::B->call() 这是 $app1->($env)
$app3 = Plack::Middleware::C->wrap($app2);  # $app3->($env) 调用 P::M::C->call() 这是 $app2->($env)
                                            # 当服务器收到一个请求时调用 $app3->($env)

 

当有新的事件发生时 – 例如, PSGI 的服务器收到一个新的请求 — 它传送这个事件给应用 app. 这个 app 是链式回调来运行每个. 这显然就是事件驱动编程的例子.

Plack::Component 和 Plack::App

Plack::Middleware 继承自 Plack::Component.所以中间件也是使用了 Plack::Component 中的东西.

Plack::Component 是用于创建 PSGI 应用的工具. 它也是很轻量一层的代码, 它主要是用于 Plack::App 名字空间的一些模块实现用的接口. Plack::App::File 是一个 Web 服务器, 用于实现指定目录的静态文件输出,Plack::App::URLMap 是用于给多个 url 指向不同的应用.  

但注意, 我们并不需要在我们的 PSGI 的应用中来指定来 use Plack::Component .  一个 PSGI 的应用只是一个代码块的引用. 这个 PSGI spec 没有讲 PSGI 的应用需要继承 Plack::Component.

我们使用 Plack::Component 提供的接口来在自己创建的应用中使用这些接口. 当我们见到 $app 时, 我们就可以使用这些行为, 这显然看起来就象中间件, 但从设计的角度来讲, 分离开来比较好. 

不过这个东西不是在我们的应用中必须的, 它会增加我们的复杂性.

 

来了就留个评论吧! 没有评论