Mojolicious模板渲染(十七)

一、后处理动态内容

虽然使用Mojolicious中的“after_dispatch”钩子后处理任务通常非常容易,但对于由渲染器生成的内容,使用Mojolicious中的“after_render”会更有效。

use Mojolicious::Lite -signatures;
use IO::Compress::Gzip qw(gzip);
 
hook after_render => sub ($c, $output, $format) {
 #做一个渲染相关钩子
 
  # 如果gzip已经设置为1,则返回
  return unless $c->stash->{gzip};
 
  # 如果user agent头接受gzip也返回
  return unless ($c->req->headers->accept_encoding // '') =~ /gzip/i;
  $c->res->headers->append(Vary => 'Accept-Encoding');
 
  # 用gzip对内容进行压缩
  $c->res->headers->content_encoding('gzip');
  gzip $output, \my $compressed;
  $$output = $compressed;
};
 
get '/' => {template => 'hello', title => 'Hello', gzip => 1};
 
app->start;
__DATA__
 
@@ hello.html.ep
<!DOCTYPE html>
<html>
  <head><title><%= title %></title></head>
  <body>Compressed content.</body>
</html>

如果我们想压缩所有动态生成的内容,也可以在Mojolicious::Renderer中激活“compress”。

二、二进制流

我们不必一次呈现所有内容,Mojolicious::Controller中的“write”方法也可以用来把呈现的内容分成一小块一小块的。

use Mojolicious::Lite -signatures;
 
get '/' => sub ($c) {
 
  # 准备请求体
  my $body = 'Hello World!';
  $c->res->headers->content_length(length $body);
 
  # 通过一个请求体来发送body
  my $drain = sub ($c) {
    my $chunk = substr $body, 0, 1, '';
    $c->write($chunk, length $body ? __SUB__ : undef);
  };
  $c->$drain;
};
 
app->start;

每当整个之前的数据块实际被写入时,就会执行drain回调函数。

HTTP/1.1 200 OK
Date: Sat, 13 Sep 2014 16:48:29 GMT
Content-Length: 12
Server: Mojolicious (Perl)
 
Hello World!

除了提供一个content-length的头,我们还可以在Mojolicious::Controller中调用“finish”,完成后手动关闭连接:

use Mojolicious::Lite -signatures;
 
get '/' => sub ($c) {
 
  # Prepare body
  my $body = 'Hello World!';
 
  # Start writing directly with a drain callback
  my $drain = sub ($c) {
    my $chunk = substr $body, 0, 1, '';
    length $chunk ? $c->write($chunk, __SUB__) : $c->finish;
 #调用finish结束
  };
  $c->$drain;
};
 
app->start;

虽然这是相当低效的。但因为它阻止了keep-alive,有时对于EventSource和类似的应用程序是必要的。

HTTP/1.1 200 OK
Date: Sat, 13 Sep 2014 16:48:29 GMT
Connection: close
Server: Mojolicious (Perl)
 
Hello World!

标签