Mojolicious-学习

参考官方教程

Hello World

使用 use Majolicious::Lite -signatures; 时, 就已经导入了 strict, warnings, utf8, 和 5.16features

生成示例代码的命令:

1
$ mojo generate lite-app myapp.pl

Commands

脚本中的:

1
2
3
4
5
## 用 @ARGV 作为其参数
app->start;

## 自己传入参数
app->start('deamon', '-l', 'http://*:8080');

应该放在代码的最后. 用来启动 Mojolicious.

Reloading

如果用 morbo 命令加载文件, Web Server 就可以热启动, 即, 你修改了脚本文件后, 不需要用 Ctrl+Ckill 暂停程序, 其自动就会加载新的内容.

1
$ morbo ./myapp.pl

Routes

路由, 下列代码为, 访问 http://127.0.0.1:3000/foo 时, 返回 Hello World!:

1
2
3
4
5
6
7
8
use Mojolicious::Lite -signatures;

## Route leading to an action that renders some text
get '/foo' => sub ($c) {
$c->render(text => 'Hello World!');
};

app->start('daemon', '-l', 'http://*:3000');

这里的 $c 是一个 Majolicious::Controller 的对象. 其包含 HTTP 的 request 和 response.

GET/POST parameter

GETPOST 的参数都传递给 Mojolicious::Controller 中的 param 函数

1
2
3
4
5
6
7
8
9
use Mojolicious::Lite -signatures;

## /foo?user=sri
get '/foo' => sub ($c) {
my $user = $c->param('user');
$c->render(text => "Hello $user.");
};

app->start;

Stash 和 templates

Mojolicious::Controller 中的 stash 用于传递数据给模板.

stash 传递的值如 template, text, data 同样会被 render 用来决定响应的内容:

如:

1
2
3
4
5
6
7
8
9
10
11
12
13
use Mojolicious::Lite -signatures;

## Route leading to an action that renders a template
get '/foo' => sub ($c) {
$c->stash(one => 23);
$c->render(template => 'magic', two => 24);
};

app->start;
__DATA__

@@ magic.html.ep
The magic numbers are <%= $one %> and <%= $two %>.

这里, render 中并没有包含 one, 但其同样被传递给了模板, 因为其在 stash 中被传递.

HTTP

Mojolicious::Controller 对象中的 req (应该是 request) 和 res (应该是 response), 用于访问 HTTP 的信息, 如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use Mojolicious::Lite -signatures;

## Access request information
get '/agent' => sub ($c) {
my $host = $c->req->url->to_abs->host;
my $ua = $c->req->headers->user_agent;
$c->render(text => "Request by $ua reached $host.");
};

## Echo the request body and send custom header with response
post '/echo' => sub ($c) {
$c->res->headers->header('X-Bender' => 'Bite my shiny metal ass!');
$c->render(data => $c->req->body);
};

app->start;

如, 这里获取访问的主机, 以及 user agent.

JSON

可查看 Mojo::JSON 来获取更多信息, 可用 Mojo::Message 和 stash value json 访问.

1
2
3
4
5
6
7
8
9
10
use Mojolicious::Lite -signatures;

## Modify the received JSON document and return it
put '/reverse' => sub ($c) {
my $hash = $c->req->json;
$hash->{message} = reverse $hash->{message};
$c->render(json => $hash);
};

app->start;

同样可以在命令行获取, 用 Mojolicious::Command::get:

1
$ ./myapp.pl get -M PUT -c '{"message":"Hello Mojo!"}' /reverse

内置的 exception 和 not-found pages

应对 404, 500 等. 其会提供一些有用的信息:

1
2
3
4
5
6
7
8
9
10
11
use Mojolicious::Lite -signatures;

## Not found (404)
get '/missing' => sub ($c) {
$c->render(template => 'does_not_exist');
};

## Exception (500)
get '/dies' => sub { die 'Intentional error' };

app->start;

同样可以用 Mojolicious::Command::get 在命令行测试:

1
$ ./myapp.pl get /dies '##error'

路由名 Route names

似乎可以用 link_to 来添加超链接.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use Mojolicious::Lite -signatures;

## Render the template "index.html.ep"
get '/' => sub ($c) {
$c->render;
} => 'index';

## Render the template "hello.html.ep"
get '/hello';

app->start;
__DATA__

@@ index.html.ep
<%= link_to Hello => 'hello' %>.
<%= link_to Reload => 'index' %>.

@@ hello.html.ep
Hello World!

布局 Layouts

没整明白.

Blocks

也没整明白.

Helpers

Helpers 指小的函数, 可以用 Mojolicious::Lite 中的 helper 关键字来创建.

如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use Mojolicious::Lite -signatures;

## A helper to identify visitors
helper whois => sub ($c) {
my $agent = $c->req->headers->user_agent || 'Anonymous';
my $ip = $c->tx->remote_address;
return "$agent ($ip)";
};

## Use helper in action and template
get '/secret' => sub ($c) {
my $user = $c->whois;
$c->app->log->debug("Request from $user");
};

app->start;
__DATA__

@@ secret.html.ep
We know who you are <%= whois %>.

Mojolicious::Plugin::DefaultHelpersMojolicious::Plugin::TagHelpers 中有很多内置的 helper 函数.

插件 Plugins

Plugins 是应用的扩展, 便于代码的分享和组织, 可以用 Mojolicious::Lite 中的 plugin 关键字来加载一个 plugin (也就是省略了 Mojolicious::Plugin:: 部分的名称).

如:

1
2
3
4
5
6
7
8
9
10
11
use Mojolicious::Lite;

plugin Config => {file => '/etc/myapp.conf', default => {foo => 'bar'}};

## Return configured foo value, or default if no configuration file
get '/foo' => sub ($c) {
my $foo = $c->app->config('foo');
$c->render(json => {foo => $foo});
};

app->start;

可以在 Mojolicious::Plugins 名称空间下找到更多 plugins.

占位符 Placeholders

用于捕获部分请求路径, 知道遇到 /., 起作用和正则表达式 [^/.]+ 类似.

捕获的值可以用 Mojolicious::Controller 中的 stash 或者 Mojolicious::Controller 中的 param 来获取:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use Mojolicious::Lite -signatures;

## /foo/test
## /foo/test123
get '/foo/:bar' => sub ($c) {
my $bar = $c->stash('bar');
$c->render(text => "Our :bar placeholder matched $bar");
};

## /testsomething/foo
## /test123something/foo
get '/<:bar>something/foo' => sub ($c) {
my $bar = $c->param('bar');
$c->render(text => "Our :bar placeholder matched $bar");
};

app->start;

可以用 <> 将占位符包裹.

Relaxed Placeholders

其和正则表达式 [^/]+ 类似, 捕获内容直到遇到 /:

1
2
3
4
5
6
7
8
9
10
11
use Mojolicious::Lite;

## /hello/test
## /hello/test.html
get '/hello/##you' => 'groovy';

app->start;
__DATA__

@@ groovy.html.ep
Your name is <%= $you %>.

Wildcard placeholders

捕获所有内容, 包括 /., 其和正则表达式 .+ 类似:

1
2
3
4
5
6
7
8
9
10
11
12
use Mojolicious::Lite;

## /hello/test
## /hello/test123
## /hello/test.123/test/123
get '/hello/*you' => 'groovy';

app->start;
__DATA__

@@ groovy.html.ep
Your name is <%= $you %>.

HTTP methods

也就是指定访问的 HTTP 方法, 如 Mojolicious::Lite 中的 getany:

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
use Mojolicious::Lite -signatures;

## GET /hello
get '/hello' => sub ($c) {
$c->render(text => 'Hello World!');
};

## PUT /hello
put '/hello' => sub ($c) {
my $size = length $c->req->body;
$c->render(text => "You uploaded $size bytes to /hello.");
};

## GET|POST|PATCH /bye
any ['GET', 'POST', 'PATCH'] => '/bye' => sub ($c) {
$c->render(text => 'Bye World!');
};

## * /whatever
any '/whatever' => sub ($c) {
my $method = $c->req->method;
$c->render(text => "You called /whatever with $method.");
};

app->start;

可选的占位符 Optional placeholders

也就是说可以赋予占位符默认值:

1
2
3
4
5
6
7
8
9
10
11
12
13
use Mojolicious::Lite -signatures;

## /hello
## /hello/Sara
get '/hello/:name' => {name => 'Sebastian', day => 'Monday'} => sub ($c) {
$c->render(template => 'groovy', format => 'txt');
};

app->start;
__DATA__

@@ groovy.txt.ep
My name is <%= $name %> and it is <%= $day %>.

严格占位符 Restrictive placeholders

给占位符提供一系列可选值:

1
2
3
4
5
6
7
8
9
10
use Mojolicious::Lite -signatures;

## /test
## /123
any '/:foo' => [foo => ['test', '123']] => sub ($c) {
my $foo = $c->param('foo');
$c->render(text => "Our :foo placeholder matched $foo");
};

app->start;

可以将占位符编译为正则表达式, 但不能使用 ^, $ 和捕获 (...), 非捕获 (?:...) 可以正常使用 :

1
2
3
4
5
6
7
8
9
10
use Mojolicious::Lite -signatures;

## /1
## /123
any '/:bar' => [bar => qr/\d+/] => sub ($c) {
my $bar = $c->param('bar');
$c->render(text => "Our :bar placeholder matched $bar");
};

app->start;

Under

只有当 under 之中返回 true value 时, 才会运行路由代码:

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
use Mojolicious::Lite -signatures;

## Authenticate based on name parameter
under sub ($c) {

## Authenticated
my $name = $c->param('name') || '';
return 1 if $name eq 'Bender';

## Not authenticated
$c->render(template => 'denied');
return undef;
};

## Only reached when authenticated
get '/' => 'index';

app->start;
__DATA__

@@ denied.html.ep
You are not Bender, permission denied.

@@ index.html.ep
Hi Bender.

其可以用来指定路径的 prefix 如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use Mojolicious::Lite;

## /foo
under '/foo';

## /foo/bar
get '/bar' => {text => 'foo bar'};

## /foo/baz
get '/baz' => {text => 'foo baz'};

## / (reset)
under '/' => {msg => 'whatever'};

## /bar
get '/bar' => {inline => '<%= $msg %> works'};

app->start;

可以用 Mojolicious::Lite 中的 group 来组织一些相关的路由:

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
use Mojolicious::Lite -signatures;

## Global logic shared by all routes
under sub ($c) {
return 1 if $c->req->headers->header('X-Bender');
$c->render(text => "You're not Bender.");
return undef;
};

## Admin section
group {

## Local logic shared only by routes in this group
under '/admin' => sub ($c) {
return 1 if $c->req->headers->header('X-Awesome');
$c->render(text => "You're not awesome enough.");
return undef;
};

## GET /admin/dashboard
get '/dashboard' => {text => 'Nothing to see here yet.'};
};

## GET /welcome
get '/welcome' => {text => 'Hi Bender.'};

app->start;

格式 Formats

文件格式能通过文件扩展如 .html 自动获取. 其用于找到对应的模板以及生成正确的 Content-Type 头.

format 和 placeholder 使用类似:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use Mojolicious::Lite -signatures;

## /detection.html
## /detection.txt
get '/detection' => [format => ['html', 'txt']] => sub ($c) {
$c->render(template => 'detected');
};

app->start;
__DATA__

@@ detected.html.ep
<!DOCTYPE html>
<html>
<head><title>Detected</title></head>
<body>HTML was detected.</body>
</html>

@@ detected.txt.ep
TXT was detected.

也可以添加可选格式:

1
2
3
4
5
6
7
8
9
10
11
use Mojolicious::Lite -signatures;

## /hello
## /hello.json
## /hello.txt
get '/hello' => [format => ['json', 'txt']] => {format => 'txt'} => sub ($c) {
return $c->render(json => {hello => 'world'}) if $c->stash('format') eq 'json';
$c->render(text => 'hello world');
};

app->start;

内容协商 Content negotiation

Mojolicious::Plugin::DefaultHelpers 中的 respond_to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use Mojolicious::Lite -signatures;

## /hello (Accept: application/json)
## /hello (Accept: application/xml)
## /hello.json
## /hello.xml
## /hello?_format=json
## /hello?_format=xml
get '/hello' => [format => ['json', 'xml']] => {format => undef} => sub ($c) {
$c->respond_to(
json => {json => {hello => 'world'}},
xml => {text => '<hello>world</hello>'},
any => {data => '', status => 204}
);
};

app->start;

静态文件 Static files

静态文件能够被内联到 DATA section 中.

1
2
3
4
5
6
7
8
9
10
use Mojolicious::Lite;

app->start;
__DATA__

@@ something.js
alert('hello!');

@@ test.txt (base64)
dGVzdCAxMjMKbGFsYWxh

@@ 之后的文件, 只包含文件扩展以及可选的 base64 解码.

外部的静态文件可以不限于一种文件类型, 且在 public 目录下的会 be served automatically.

1
2
3
$ mkdir public
$ mv something.js public/something.js
$ mv mojolicious.tar.gz public/mojolicious.tar.gz

其具有很高的优先级.

外部模板 External templates

renderer 会在 templates 目录下寻找外部模板.

1
2
$ mkdir -p templates/foo
$ echo 'Hello World!' > templates/foo/bar.html.ep

其比 DATA section 下的模板的优先级更高.

1
2
3
4
5
6
7
8
use Mojolicious::Lite -signatures;

## Render template "templates/foo/bar.html.ep"
any '/external' => sub ($c) {
$c->render(template => 'foo/bar');
};

app->start;

Home

可以用 Mojolicious 中的 home 和 application 的 home directory (也就是来查找 publictemplate 的目录) 交互. 可以将和 application 相关的数据都放在这个目录下.

Mojo::Home 下有很多继承自 Mojo::File 的有用方法, 如 child, slurp.

1
2
3
4
5
6
7
8
9
use Mojolicious::Lite -signatures;

## Load message into memory
my $hello = app->home->child('cache', 'hello.txt')->slurp;

## Display message
get '/' => sub ($c) {
$c->render(text => $hello);
};

状态 Conditions

Conditions 如 agenthost, 允许更强大的路由结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use Mojolicious::Lite -signatures;

## Firefox
get '/foo' => (agent => qr/Firefox/) => sub ($c) {
$c->render(text => 'Congratulations, you are using a cool browser.');
};

## Internet Explorer
get '/foo' => (agent => qr/Internet Explorer/) => sub ($c) {
$c->render(text => 'Dude, you really need to upgrade to Firefox.');
};

## http://mojolicious.org/bar
get '/bar' => (host => 'mojolicious.org') => sub ($c) {
$c->render(text => 'Hello Mojolicious.');
};

app->start;

会话 Sessions

需记住, 所有的 session 数据都会用 Mojo::JSON 格式化, 然后用加密签名存储在 client-side 以防止篡改.

1
2
3
4
5
6
7
8
9
10
11
12
use Mojolicious::Lite -signatures;

## Access session data in action and template
get '/counter' => sub ($c) {
$c->session->{counter}++;
};

app->start;
__DATA__

@@ counter.html.ep
Counter: <%= session 'counter' %>

还可以使用 Mojolicious 中的 secrets, 如:

1
app->secrets(['My secret passphrase here']);

上传文件 File uploads

用户代理 User agent

Mojo::UserAgentMojolicious::Plugin::DefaultHelpers 中的 ua

网络套接字 WebSockets

Mode

Testing

可参考 Test::Mojo

官方示例解析


Mojolicious-学习
http://example.com/2023/02/11/Mojolicious-学习/
作者
Jie
发布于
2023年2月11日
许可协议