Mojolicious-Growing-文档

索引文档, 查看 perldoc Mojolicious::Guides

Mojolicious 的结构为:

  • controller 从 用户处获取 request
  • controller 将数据传递给 Model, 并从其获取处理后的数据
  • controller 将数据传递给 View, 并获取 response

Sessions

一般来说, 所有的 session 数据存储在服务器端, 浏览器通过 session IDs 来和服务器交换数据.

在 Mojolicious 中:

1
Set-Cookie: session=hmac-sha256(base64(json($session)))

Prototype

一个完整的 Mojolicious 应用的结构为:

可以用下列命令构建出这个目录结构:

1
2
mojo generate lite-app myapp.pl 
mojo generate app MyAppName

Foundation

在构建的目录下开始编写:

1
2
3
4
mkdir myapp
cd myapp
touch myapp.pl
chmod 744 myapp.pl

myall.pl 中写入:

1
2
3
4
5
6
7
8
!/usr/bin/env perl
use Mojolicious::Lite -signatures;

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

app->start;

-signatures 用于启用 subroutine 的新语法支持.

运行程序:

1
morbo ./myapp.pl

模块 model

在 Mojolicious 中的模块处理:

1
2
3
mkdir -p lib/MyApp/Model
touch lib/MyApp/Model/Users.pm
chmod 644 lib/Myapp/Model/Users.pm

lib/Myapp/Model/Users.pm 文件中添加:

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
package MyApp::Model::Users;

use strict;
use warnings;
use experimental qw(signatures);

use Mojo::Util qw(secure_compare);

my $USERS = {
joel => 'las3rs',
marcus => 'lulz',
sebastian => 'secr3t'
};

sub new ($class) { bless {}, $class }

sub check ($self, $user, $pass) {

# Success
return 1 if $USERS->{$user} && secure_compare $USERS->{$user}, $pass;

# Fail
return undef;
}

1;

helper 来注册一个函数, 来让这个 model 对所有的模板起作用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
!/usr/bin/env perl
use Mojolicious::Lite -signatures;

use lib qw(lib);
use MyApp::Model::Users;

Helper to lazy initialize and store our model object
helper users => sub { state $users = MyApp::Model::Users->new };

/?user=sebastian&pass=secr3t
any '/' => sub ($c) {

# Query parameters
my $user = $c->param('user') || '';
my $pass = $c->param('pass') || '';

# Check password
return $c->render(text => "Welcome $user.") if $c->users->check($user, $pass);

# Failed
$c->render(text => 'Wrong username or password.');
};

app->start;

注册的函数为 users, 所有的 $c 都可以调用这个 helper 函数.

关于 $c 对象

在Mojolicious中,$c 是一个代表当前请求的控制器对象 (Controller object). 它是在Mojolicious应用程序中处理HTTP请求的关键对象之一,可以访问请求和响应对象,路由参数,模板引擎等。它提供了许多方法来处理HTTP请求和响应,例如$c->param用于获取查询参数,$c->render用于呈现视图模板或返回响应,$c->redirect_to用于重定向到其他URL等。在你的代码中,$c 对象被用于获取查询参数和呈现响应。

param() 用于获取查询参数.

any 用于匹配 HTTP 请求方法, 如 POST, GET.

测试 Testing

创建测试目录:

1
2
3
mkdir t
touch t/login.t
chmod 644 t/login.t

使用 Test::Mojo 模块, 如:

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
28
29
30
31
32
33
34
use Test::More;
use Test::Mojo;

Include application
use Mojo::File qw(curfile);
require(curfile->dirname->sibling('myapp.pl'));

Allow 302 redirect responses
my $t = Test::Mojo->new;
$t->ua->max_redirects(1);

Test if the HTML login form exists
$t->get_ok('/')
->status_is(200)
->element_exists('form input[name="user"]')
->element_exists('form input[name="pass"]')
->element_exists('form input[type="submit"]');

Test login with valid credentials
$t->post_ok('/' => form => {user => 'sebastian', pass => 'secr3t'})
->status_is(200)
->text_like('html body' => qr/Welcome sebastian/);

Test accessing a protected page
$t->get_ok('/protected')->status_is(200)->text_like('a' => qr/Logout/);

Test if HTML login form shows up again after logout
$t->get_ok('/logout')
->status_is(200)
->element_exists('form input[name="user"]')
->element_exists('form input[name="pass"]')
->element_exists('form input[type="submit"]');

done_testing();

要检测的文件用 require(curfile->dirname->sibling('myapp.pl')); 这一行指定.

测试如:

1
2
3
prove -l
prove -l t/login.t
prove -l -v t/login.t

request 来调试:

1
2
./myapp.pl get /
./myapp.pl get -v '/?user=sebastian&pass=secr3t'

保持状态 State keeping

创建 session, 以及获取 session 的用户:

1
2
$c->session(user => 'sebastian');
my $user = $c->session('user');

$c->session('user'); 中的 user 应该是 hash 的键值.

可以为 session 创建 secure passphrase:

1
$app->secrets(['Mojolicious rocks']);

设置 sessions 的 expiration 时限:

1
$c->session(expiration => 3600);

删除一个 session:

1
$c->session(expires => 1);

一个较为完整的示例:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
!/usr/bin/env perl
use Mojolicious::Lite -signatures;

use lib qw(lib);
use MyApp::Model::Users;

Make signed cookies tamper resistant
app->secrets(['Mojolicious rocks']);

helper users => sub { state $users = MyApp::Model::Users->new };

Main login action
any '/' => sub ($c) {

# Query or POST parameters
my $user = $c->param('user') || '';
my $pass = $c->param('pass') || '';

# Check password and render "index.html.ep" if necessary
return $c->render unless $c->users->check($user, $pass);

# Store username in session
$c->session(user => $user);

# Store a friendly message for the next page in flash
$c->flash(message => 'Thanks for logging in.');

# Redirect to protected page with a 302 response
$c->redirect_to('protected');
} => 'index';

Make sure user is logged in for actions in this group
group {
under sub ($c) {

# Redirect to main page with a 302 response if user is not logged in
return 1 if $c->session('user');
$c->redirect_to('index');
return undef;
};

# A protected page auto rendering "protected.html.ep"
get '/protected';
};

Logout action
get '/logout' => sub ($c) {

# Expire and in turn clear session automatically
$c->session(expires => 1);

# Redirect to main page with a 302 response
$c->redirect_to('index');
};

app->start;
__DATA__

@@ index.html.ep
% layout 'default';
%= form_for index => begin
% if (param 'user') {
<b>Wrong name or password, please try again.</b><br>
% }
Name:<br>
%= text_field 'user'
<br>Password:<br>
%= password_field 'pass'
<br>
%= submit_button 'Login'
% end

@@ protected.html.ep
% layout 'default';
% if (my $msg = flash 'message') {
<b><%= $msg %></b><br>
% }
Welcome <%= session 'user' %>.<br>
%= link_to Logout => 'logout'

@@ layouts/default.html.ep
<!DOCTYPE html>
<html>
<head><title>Login Manager</title></head>
<body><%= content %></body>
</html>

Inflating templates

__DATA__ section 的内容以不同文件的方式转换到 templatespublic 目录下, 如:

1
./myapp.pl inflate

应该可以自定义.


Mojolicious-Growing-文档
http://example.com/2023/05/17/Mojolicious-Growing-文档/
作者
Jie
发布于
2023年5月17日
许可协议