Web编程框架-Mojolicious
2011-02-28 12:30:42 阿炯

Mojolicious 是下一代的 Perl 语言的 Web 编程框架,提供强大的功能而无需第三方包依赖,功能包括:RESTful 的路由、插件管理、Perl风格的模板系统、会话管理、签名的 cookie、静态文件服务、测试框架、文件级的Unicode支持等等。采用Artistic协议授权。

同时 Mojolicious 也是一个全堆栈的 HTTP/1.1 和 WebSocket 的客户端和服务器端的实现,还包括 TLS、Gonjour、IDNA、Comet、Chunking和 multipart支持。内建支持 Linux epoll、kqueue以及Unix Domain套接字的Web服务器,自动检测 CGI, FastCGI 和 PSGI。提供 JSON 以及 XML/HTML5 的解析器,支持 CSS3 的选择器。原生的非阻塞 non-blocking I/O 的 Web 服务器, 支持多种事件循环, 支持 prefork 和热部署, 可以完美的实现高可用的 Web 服务。你可以使用单个模块文件 Mojolicious::Lite 来开发成单个文件的网络应用的原型。
 
早期互联网很多人熟悉并了解 Perl 是因为它是有非常优秀的 CGI 的 lib 库,使用 CGI 模块当时并不同于其它的语言,当时这个不需要开发者掌握太多的 Perl 语言语法规则,并且能够让人们在学习和开发应用程序的过程中感受 Perl 的乐趣。虽然以我们现在的眼光来看以前的这种开发方法已经不在适用于现在的 Web 时代,但是 CGI 的开发思想却对我们有很深的影响。Mojolicious 正是实现了新的开发思想在而产生的新的开发技术。



A next generation web framework for the Perl programming language.

Features

* An amazing MVC web framework supporting a simplified single file mode through Mojolicious::Lite.Powerful out of the box with RESTful routes, plugins, Perl-ish templates, session management, signed cookies, testing framework, static file server, I18N, first class unicode support and much more for you to discover.
* Very clean, portable and Object Oriented pure Perl API without any hidden magic and no requirements besides Perl 5.
* Full stack HTTP 1.1 and WebSocket client/server implementation with IPv6, TLS, Bonjour, IDNA, Comet (long polling), chunking and multipart support.
* Builtin async IO web server supporting epoll, kqueue, UNIX domain sockets and hot deployment, perfect for embedding.
* Automatic CGI, FastCGI and PSGI detection.
* JSON and XML/HTML5 parser with CSS3 selector support.
* Fresh code based upon years of experience developing Catalyst.

* Mojolicious 框架是一个非常简洁, 轻巧. 并完全面向对象的纯 Perl 的 API. 建议基于 Perl 5.20.0 ( Perl 5.10.1 也可以使用, 但可能会需要一些其它的 cpan的模块).
* 全功能的 HTTP 和 WebSocket 的服务器和客户端实现. 支持 IPv6, TLS, SNI, IDNA, HTTP/SOCKS5 proxy, Comet (long polling), keep-alive, 连接池, 超时, Cookie, Multipart 和 Gzip 压缩的支持.
* 原生的非阻塞 non-blocking I/O 的 Web 服务器, 支持多种事件循环, 支持 prefork 和热部署, 可以完美的实现高可用的 Web 服务.
* 支持 JSON 和 HTML/XML 解析并支持使用 CSS 的选择器.
* 基于多年的 Catalyst 开发经验来开发的这个系统.


这里对比了Mojo与Mojolicious的一些异同(功能、属性、方法等方面),转自JSON_NULL的博客,感谢原作者


Mojo

Mojo是一个强大的WEB开发工具包,它包含写简单Web应用和写高度分层的Web框架(如:Mojolicious)所需的所有基本工具包和接口类。常用的工具包括Mojo::UserAgent,Mojo::DOM,Mojo::JSON,Mojo::Server::Daemon,Mojo::Server::Prefork,Mojo::IOLoop和Mojo::Template。

属性

Mojo包中包含的属性有:home,log、ua。下面分别进行介绍。

home

应用程序的家目录,默认情况下是Mojo::Home对象。
使用的语法如下:
my $home = $app->home;
$app = $app->home(Mojo::Home->new);

log

应用程序的日志等级,默认情况下是一上 Mojo::Log对象。
使用语法如下:
my $log = $app->log;
$app = $app->log(Mojo::Log->new);

ua

一个全功能的HTTP用户代码,默认情况下是Mojo::UserAgent的对象。
使用语法如下:
my $ua = $app->ua;
$app = $app->ua(Mojo::UserAgent->new);

方法

Mojo模块从Mojo::Base模块继承得到了所有方法,并实现了以下方法:build_tx,conifg,handler;下面分别介绍。

build_tx

事务的创建者,默认情况下创建一个 Mojo::Transaction::HTTP对象。
使用语法如下:
my $tx = $app->build_tx;

想要了解更多关于 Mojo::Transaction::HTTP模块的信息,请移步:Mojo::Transaction::HTTP。

config

负责应用程序的配置,语法如下:
my $hash = $app->config;
my $foo  = $app->config('foo');
$app = $app->config({foo => 'bar', baz => 23});
$app = $app->config(foo => 'bar', baz => 23);

# Remove value
my $foo = delete $app->config->{foo};

# Assign multiple values at once
$app->config(foo => 'test', bar => 23);

当传一个字符串作为参数时,这个参数的值被作为$key,然后从$app对象下的config属性中取键名为$key的$value并返回。得到的其实就是$app->{config}->{$key}的值。当传一个hash引用作为参数或传一个列表作为参数时,会把hash引用(或把参数列表转换为hash引用后)合并到$app->{config}中,如果$app->{config}中已经有对应的键了,则会用新的值覆盖它。从而完成设置config的目的。

handler

应用程序的主要入口点,在这里会创建每一个新的事务。这通常会是一个Mojo::Transaction::HTTP或Mojo::Tracsaction::HTTP::WebSocket对象,这个方法需要在子类中重载。


Mojolicious

简介
# Application
package MyApp;
use Mojo::Base 'Mojolicious';

# Route
sub startup {
  my $self = shift;
  $self->routes->get('/hello')->to('foo#hello');
}

# Controller
package MyApp::Controller::Foo;
use Mojo::Base 'Mojolicious::Controller';

# Action
sub hello {
  my $self = shift;
  $self->render(text => 'Hello World!');
}

这个包实现基于Mojo工具包,实现了一个实时WEB框架。支持RESTful路由,插件功能、命令管理、Perl-ish模板、内容协商、会话管理、表单验证、测试框架、静态文件服务、CGI/PSGI检测、Unicode 等。

事件钩子

Mojolicious中实现了以下事件,下面按照它们被触发的顺序一一介绍。

after_build_tx

这个点是工作在 HTTP 的请求还没有被解析, 但连接已经建立的时候。
$app->hook(after_build_tx => sub {
  my ($tx, $app) = @_;
  ...
});

这个hook的功能非常强大,但一般情况下是用不到的。可以使用它来完成诸如上传进度条等高级功能。

注:这个事件不适用于嵌入式应用程序,因为只有主机应用程序可以构建事务。( 默认参数送的是 transaction 和 application object )

around_dispatch

在接收到“请求”并且对所有“dispatch”过程包装完成之后触发。如果你需要继续执行后面的控制链,则需要手动地执行$next->();在$next->()被调用的前后的代码会在不同的时机被运行。
$app->hook(around_dispatch => sub {
  my ($next, $c) = @_;
  ...
  $next->();
  ...
});

这个hook也不常用,但它的功能非常强大,可以让你定制程序中的所有异常行为(404,500等都可以在这个hook点返回)。 (传送的参数是下一个 hook 点的回调和 controller 的对象)

before_dispatch

这个点是工作在静态文件调度和路由选择之前。
$app->hook(before_dispatch => sub {
  my $c = shift;
  ...
});

如果你要重写进来的请求和提前做一些处理,这个hook点非常有用。 ( 默认参数送的是 controller 控制器对象 )

after_static

这个hook在静态响应已经由静态文件服务生成之后触发。
$app->hook(after_static => sub {
  my $c = shift;
  ...
});

主要用来对静态响应做些后处理 ( post-processing )。 默认参数送的是 controller 对象 。

before_routes

这个hook在静态文件服务器确定是否应该提供静态文件并在路由启动之前触发。
$app->hook(before_routes => sub {
  my $c = shift;
  ...
});

多用于自定义调度器和收集度量用。( 默认参数是 controller 对象 )

around_action

当一个 action 被调用的时候, 触发这个hook。 如果你需要继续执行后面的控制链,则需要手动地执行$next->();默认情况下 对action的调用是最控制链中的后一个hook,所以你的代码需要放在$next->()之前。
$app->hook(around_action => sub {
  my ($next, $c, $action, $last) = @_;
  ...
  return $next->();
});

这个hook功能强大但并不常用,它可以让你传递额外的一些参数给 action 并对不同的处理方式返回不同的值。参数为:下一个hook点,当前控制器对象,action,是否为最后一个hook。

注:如果有嵌套路由,该hook可以针对相同的请求多次触发。

before_render

在内容由渲染器生成之前触发。
$app->hook(before_render => sub {
  my ($c, $args) = @_;
  ...
});

主要用于预处理传递给渲染器的参数。接收的参数有:当前控制器对象,将要传递给渲染器的参数。

注:这个hook可能会触发失败。

after_render

在内容由渲染器生成并分配给响应之后触发。
$app->hook(after_render => sub {
  my ($c, $output, $format) = @_;
  ...
});

主要用于后处理动态生成的内容。接收的参数有:当前控制器对象,生成内容的引用,格式。

注:这个hook可能会触发失败。

after_dispatch

在响应被生成之后以倒序触发。
$app->hook(after_dispatch => sub {
  my $c = shift;
  ...
});

这个主要用来重写响应的输出和其它的处理任务。(默认参数送的是 controller 对象)

注:这个hook可能会触发失败。

属性

Mojolicious从Mojo继承所有属性,并实现以下属性。

commands

my $commands = $app->commands;
$app = $app->commands(Mojolicious::Commands->new);

应用程序中的命令行接口,默认为Mojolicious::Commands对象。
# Add another namespace to load commands from
push @{$app->commands->namespaces}, 'MyApp::Command';

controller_class
my $class = $app->controller_class;
$app = $app->controller_class('Mojolicious::Controller');

默认控制器的类,默认为Mojolicious::Controller。

注:此类需要在第一个请求到达之前已经被加载。

log
my $log = $app->log;
$app = $app->log(Mojo::Log->new);

应用程序的日志记录对象,默认为Mojo::Log对象。如果应用运行在development “模式”,则日志的等级为debug,否则日志的等级将被设置为info。

如果log目录存在,所有日志信息会被写入log/$mode.log文件中,否则日志信息会被写入STDERR流中。
# Log debug message
$app->log->debug('It works');
max_request_size
my $max = $app->max_request_size;
$app = $app->max_request_size(16777216);

最大“请求”的大小(以字节为单位),默认为Mojo::Message中max_message_size的值。将值设置为0表示允许无限大的“请求”。

注:如果您使用Mojo::Message中的dom或json方法解析请求体,此值设置的过大会使内存使用量增加。

mode

my $mode = $app->mode;
$app = $app->mode('production');

应用程序的运行模式,默认情况下使用环境变量MOJO_MODE和PLACK_ENV的值,如果它们不存在则给默认值development。

moniker

my $moniker = $app->moniker;
$app = $app->moniker('foo_bar');

应用程序的 moniker,通常用作配置文件的默认文件名,默认使用Mojo::Util中的decamelize对应用程序的类进行处理得到。

plugins

my $plugins = $app->plugins;
$app = $app->plugins(Mojolicious::Plugins->new);

应用程序的插件管理器,默认为Mojolicious::Plugins对象。如果要加载插件,请参阅plugin方法。
# Add another namespace to load plugins from
push @{$app->plugins->namespaces}, 'MyApp::Plugin';

renderer

my $renderer = $app->renderer;
$app = $app->renderer(Mojolicious::Renderer->new);

用于内容渲染的对象,默认为Mojolicious::Renderer对象。有关如何生成内容的更多信息,请参阅Mojolicious :: Guides :: Rendering。
# Add another "templates" directory
push @{$app->renderer->paths}, '/home/sri/templates';

# Add another "templates" directory with higher precedence
unshift @{$app->renderer->paths}, '/home/sri/themes/blue/templates';

# Add another class with templates in DATA section
push @{$app->renderer->classes}, 'Mojolicious::Plugin::Fun';

routes

my $routes = $app->routes;
$app = $app->routes(Mojolicious::Routes->new);

应用程序的路由器,默认为Mojolicious::Routes对象。您可以在startup方法中使用此对象对url的路由规则进行定义。
# Add routes
my $r = $app->routes;
$r->get('/foo/bar')->to('test#foo', title => 'Hello Mojo!');
$r->post('/baz')->to('test#baz');

# Add another namespace to load controllers from
push @{$app->routes->namespaces}, 'MyApp::MyController';

secrets

my $secrets = $app->secrets;
$app = $app->secrets([$bytes]);

应用中用于对cookie进行签名的密码口令,默认使用的是使用的名称“moniker”。如果你没有设置而使用了默认的“moniker”应用会在日志中提示是这是不安全的,需要修改你的secrets。

你可以设置多个密码,只有第一个用于创建新的签名,但所有的密码都会被用于验证。所以当你想要更换密码时,可以把新的密码放在第一个位置,后面放老的密码。这样不会使已经签名的cookie失效。

session

my $sessions = $app->sessions;
$app = $app->sessions(Mojolicious::Sessions->new);

基于cookie的会话管理器,默认为Mojolicious::Session对象。
# Change name of cookie used for all sessions
$app->sessions->cookie_name('mysession');

static

my $static = $app->static;
$app = $app->static(Mojolicious::Static->new);

用于从public目录中提取静态文件,默认为Mojolicious::Static对象。
# Add another "public" directory
push @{$app->static->paths}, '/home/sri/public';

# Add another "public" directory with higher precedence
unshift @{$app->static->paths}, '/home/sri/themes/blue/public';

# Add another class with static files in DATA section
push @{$app->static->classes}, 'Mojolicious::Plugin::Fun';

# Remove built-in favicon
delete $app->static->extra->{'favicon.ico'};

types

my $types = $app->types;
$app = $app->types(Mojolicious::Types->new);

负责对MIME类型的扩展名进行解析,默认为:Mojolicious::Types对象。
# Add custom MIME type
$app->types->type(twt => 'text/tweet');

validator

my $validator = $app->validator;
$app = $app->validator(Mojolicious::Validator->new);

表单验证器对象,默认为Mojolicious::Validator对象。
# Add validation check
$app->validator->add_check(foo => sub {
  my ($validation, $name, $value) = @_;
  return $value ne 'foo';
});

# Add validation filter
$app->validator->add_filter(quotemeta => sub {
  my ($validation, $name, $value) = @_;
  return quotemeta $value;
});

方法

Mojolicious从Mojo继承所有方法,并实现以下方法。

build_controller

my $c = $app->build_controller;
my $c = $app->build_controller(Mojo::Transaction::HTTP->new);
my $c = $app->build_controller(Mojolicious::Controller->new);

使用controller_class构建默认的控制器对象。
# Render template from application
my $foo = $app->build_controller->render_to_string(template => 'foo');

build_tx

my $tx = $app->build_tx;

构建mojo::Trasaction::HTTP对象,并触发after_build_tx事件hook。

defaults

my $hash = $app->defaults;
my $foo  = $app->defaults('foo');
$app = $app->defaults({foo => 'bar', baz => 23});
$app = $app->defaults(foo => 'bar', baz => 23);

对所有“请求”绑定默认值到Mojolicious::Controller中的stash中。
# Remove value
my $foo = delete $app->defaults->{foo};

# Assign multiple values at once
$app->defaults(foo => 'test', bar => 23);

dispatch

$app->dispatch(Mojolicious::Controller->new);

这是每个Mojolicious应用程序都会有的核心,为每个请求调用“static”和“routes”调度器,并传递一个Mojoliclious::Controller对象。

handler

$app->handler(Mojo::Transaction::HTTP->new);
$app->handler(Mojolicious::Controller->new);

设置默认控制器,并为每个“请求”触发“around_dispatch”事件hook。

helper

$app->helper(foo => sub {...});

添加或替换一个在应用程序控制器和应用对象中可以调用的helper方法或对象。当然在ep模块中也可以使用。有关默认情况下可用的帮助器的完整列表,请参阅Mojolicious::Plugin::DefaultHelpers和Mojolicious::Plugin::TagHelpers。
# Helper
$app->helper(cache => sub { state $cache = {} });

# Application
$app->cache->{foo} = 'bar';
my $result = $app->cache->{foo};

# Controller
$c->cache->{foo} = 'bar';
my $result = $c->cache->{foo};

# Template
% cache->{foo} = 'bar';
%= cache->{foo}

hook

$app->hook(after_dispatch => sub {...});

使用hook来扩展Mojolicious,允许你的代码与所有请求无缝的共享。以前的“事件钩子”小节已经对可用的hook进行了详细的介绍。
# Dispatchers will not run if there's already a response code defined
$app->hook(before_dispatch => sub {
  my $c = shift;
  $c->render(text => 'Skipped static file server and router!')
    if $c->req->url->path->to_route =~ /do_not_dispatch/;
});

new

my $app = Mojolicious->new;
my $app = Mojolicious->new(moniker => 'foo_bar');
my $app = Mojolicious->new({moniker => 'foo_bar'});

构建一个新的Mojolicious应用程序并调用startup方法。会自动检测您的主目录,还会设置渲染器,静态文件服务器,一级默认的插件和一个around_dispatch钩子。并使用默认的异常处理。

plugin

$app->plugin('some_thing');
$app->plugin('some_thing', foo => 23);
$app->plugin('some_thing', {foo => 23});
$app->plugin('SomeThing');
$app->plugin('SomeThing', foo => 23);
$app->plugin('SomeThing', {foo => 23});
$app->plugin('MyApp::Plugin::SomeThing');
$app->plugin('MyApp::Plugin::SomeThing', foo => 23);
$app->plugin('MyApp::Plugin::SomeThing', {foo => 23});

加载一个插件,有关Mojolicious发行版中包含的示例插件的完整列表,请参阅Mojolicious::Plugins中的“PLUGINS”。

start

$app->start;
$app->start(@ARGV);

启动应用程序的命令行界面。有关默认可用命令的完整列表,请参阅Mojolicious::Commands中的“COMMANDS”。注意选项-h/ --help,--home和-m/ --mode,其通过所有命令共享,将在编译时对@ARGV进行解析。
# Always start daemon
$app->start('daemon', '-l', 'http://*:8080');

startup

$app->startup;

这是应用程序中最重要的一个钩子,它将在应用程序启动时调用,需要在子类中重载。
sub startup {
  my $self = shift;
  ...
}

AUTOLOAD

除了上述的“ATTRIBUTES”和“METHODS”之外,您还可以在Mojolicious对象上调用helpers。这包括Mojolicious::Plugin::DefaultHelpers和Mojolicious::Plugin::TagHelpers中的所有helpers。请注意,应用程序helpers总是使用新的默认控制器对象调用,因此它们不能依赖或更改控制器状态,包括请求,响应和存储。
# Call helper
say $app->dumper({foo => 'bar'});

# Longer version
say $app->build_controller->helpers->dumper({foo => 'bar'});

框架中捆绑的文件

该Mojolicious发布版包括了已捆绑在内部使用的一些文件,如下表所示。

名称     版权信息     开源协议
Mojolicious Artwork     Copyright (C) 2010-2017, Sebastian Riedel.     Licensed under the CC-SA License, Version 4.0
jQuery     Copyright (C) jQuery Foundation.     Licensed under the MIT License
prettify.js     Copyright (C) 2006, 2013 Google Inc..     Licensed under the Apache License, Version 2.0



最新版本:6.2
重新设计:所有新异常和 not_found 页面
表单验证:更简单,可扩展
CSRF 保护
Template variants: 不仅仅是响应式 web 设计
添加了新 Mojo::DOM:支持不同的 node 类型和操作 HTML 的新方式
New hook: around_action 和 before_render
SO_REUSEPORT
Rotating secrets
Non-blocking bridges
Cheap helpers
任意地方都可以选择占位符
RFC 7159:支持新 JSON spec.
permessage-deflate
更多内容请看发行说明


官方主页:http://mojolicious.org/

该文章最后由 阿炯 于 2020-10-01 22:02:11 更新,目前是第 2 版。