TABLE OF CONTENTS

文档

Mojolicious::Guides::Growing - 成长成更大型的项目

概述

本文档介绍中, 我们来讲怎么从 Mojolicious::Lite 开始做的原型项目, 成长为一个结构良好的的全或功能 Mojolicious 应用.

本文档更新到版本 6.05

概念

所有 Mojolicious 的开发者都需要知道.

Model View Controller

MVC 是一种现代的软件体系结构的模式, 它起源于 Smalltalk-80 的图形界面编程, 用于分离应用程序逻辑, 表示和输入.

         +------------+    +-------+    +------+
Input -> | Controller | -> | Model | -> | View | -> Output
         +------------+    +-------+    +------+

目前基本只需要给现在有程序小量的修改就可以转到 controller 这种模式上来. 目前基本每个 Web 的框架都是基于 MVC 的结构, 包括 Mojolicious.

            +----------------+     +-------+
Request  -> |                | <-> | Model |
            |                |     +-------+
            |   Controller   |
            |                |     +-------+
Response <- |                | <-> | View  |
            +----------------+     +-------+

在上面这个结构中 controller 接收到用户的请求后, 传送这些数据给 model 处理完数据, 然后通过 view 转化为实际的响应. 但要注意, 这种模式只是一个指导方针, 最重要的目标是有更干净和易于维护的代码.

REpresentational State Transfer ( REST )

RESR 是一种 Web 软件架构的风格, 近来常常用于给 HTTP 做协议. 在 REST 中, 你可以打开 http://mojolicio.us/foo 这个 URL 在你的浏览器中, 你相当于告诉你的 Web 服务来进行一个 HTML 的表示这个地址的资源.

+--------+                                +--------+
|        | -> http://mojolicio.us/foo  -> |        |
| Client |                                | Server |
|        | <- <html>Mojo rocks!</html> <- |        |
+--------+                                +--------+

这里的基本思想是, 所有的资源都是有一个唯一的 URL 来进行查寻, 并且每个资源都可以有不同的表示方式, 如 HTML, RSS 和 JSON . 让界面从数据层分离出来只需要注意和用户会议的状态进行互交.

+---------+                        +------------+
|         | ->    PUT /foo      -> |            |
|         | ->    Hello world!  -> |            |
|         |                        |            |
|         | <-    201 CREATED   <- |            |
|         |                        |            |
|         | ->    GET /foo      -> |            |
| Browser |                        | Web Server |
|         | <-    200 OK        <- |            |
|         | <-    Hello world!  <- |            |
|         |                        |            |
|         | ->    DELETE /foo   -> |            |
|         |                        |            |
|         | <-    200 OK        <- |            |
+---------+                        +------------+

虽然 HTTP 的方法象 PUT, GETDELETE 并不是 REST 的一部分, 但他们用来管理资源非常不错.

会话 (Sessions)

Web 服务上的 HTTP 本来就被设计成一个无状态的协议, 所以我们并不知道是否是以前的请求, 这使得让用户友好登录到系统变得非常棘手. Sessions 就是用来解决这个问题, 使得网络应用中跨多个 HTTP 请求后还能保留状态信息.

GET /login?user=sri&pass=s3cret HTTP/1.1
Host: mojolicio.us

HTTP/1.1 200 OK
Set-Cookie: sessionid=987654321
Content-Length: 10
Hello sri.

GET /protected HTTP/1.1
Host: mojolicio.us
Cookie: $Version=1; sessionid=987654321

HTTP/1.1 200 OK
Set-Cookie: sessionid=987654321
Content-Length: 16
Hello again sri.

传统的上, 所有的 session 会话数据存储在服务器端的, 只有 Session 中会话的 ID 以 Cookie 的形式在 Web 服务器和浏览器之间交换.

HTTP/1.1 200 OK
Set-Cookie: session=base64(hmac-sha1(json($session)))

Mojolicious 中对 session 进行了进一步的处理, 使用 HMAC-SHA1 签署 cookie, 这更兼容 REST 的理念并降低一些其它的要求.

测试驱动开发(Test Driven Development)

TDD, 是一种不同于传统软件开发流程的新型的开发方法. 它要求在编写某个功能的代码之前先编写测试代码, 然后只编写使测试通过的功能代码, 通过测试来推动整个开发的进行. 这有助于编写简洁可用和高质量的代码, 并加速开发过程. 有许多优点, 如始终具有良好的测试覆盖率和代码的可测试性设计, 这将反过来往往防止未来的变化影响旧的代码的功能. 大多的 Mojolicious 使用 TDD 开发;

原型

Mojolicious 和其他 Web 框架的主要区别之一是, 它还包括有一个 Mojolicious::Lite, 这是一个微型的 Web 框架, 为快速实现原型优化过的.

差异

你有一些很好的主意, 有很酷的想法, 你想尽可能快地尝试实现它, 这也是为什么 Mojolicious::Lite 写的应用程序不需要超过一个单个文件的原因.

myapp.pl   # Templates and even static files can be inlined

全功能的 Mojolicious 应用程序更加接近象一个 CPAN 的发布包一样, 有着良好结构, 以最大限度的提高可维护性.

myapp                      # 应用程序的目录 
|- script                  # 脚本的目录 
|  +- myapp                # 应用程序的脚本  
|- lib                     # Library 的目录 
|  |- MyApp.pm             # 应用程序的类 
|     +- Controller        # 控制器名字空间
|        +- Example.pm     # 控制器的类 
|- t                       # 测试目录 
|  +- basic.t              # Random test
|- log                     # 日志目录 
|  +- development.log      # 开发模式的日志
|- public                  # 静态文件的目录 (象 css, js 之类)
|  +- index.html           # 静态 HTML 文件
+- templates               # 模板目录
   |- layouts              # 模板目录和 layout
   |  +- default.html.ep   # Layout 模板 
   +- example              # "Example"  controller 的模板目录
      +- welcome.html.ep   # "welcome" 动作的模板

这二种应用类型的骨架, 可以使用 generate 来自动生成, 我们只需要使用命令 Mojolicious::Command::generate::lite_appMojolicious::Command::generate::app.

$ mojo generate lite_app
$ mojo generate app

基础

我们启动我们新的应用程序需要使用一个可执行的文件

$ mkdir myapp
$ cd myapp
$ touch myapp.pl
$ chmod 744 myapp.pl

这是我们 login manager 的样例程序的一个基础.

#!/usr/bin/env perl
use Mojolicious::Lite;

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

app->start;

内置的 Web 开发服务器有个非常好的地方, 就是会自动的在你的程序变化更新后重新加载.

$ morbo myapp.pl
Server available at http://127.0.0.1:3000.

当你保存你的修改后, 会在你下一次刷新浏览器的时候就会自动生效了.

A birds-eye view

我们可以通过浏览器发送一个 HTTP 请求, 就象下面这样

GET / HTTP/1.1
Host: localhost:3000

一旦请求在 Web 服务器上通过事件循环被接收到后, 接下来会通过 Mojolicious 进行几个简单的处理.

1. 如果请求的只是一个静态文件就行, 就输出这个文件.
2. 尝试找一个能满足要求的路由信息.
3. 调度这个请求到这个路由上指定的一个或者多个方法和动作.
4. 处理请求, 可能产生的渲染器的响应. 
5. 将控制器内返回到 Web 服务器, 如果没有响应生成, 就会通过事件循环来等一个非阻塞操作.

如果你的应用进入了路由中第二步, 和第四步, 就会响应一些内容, 就象下面这个一样, 并在你的浏览器上显示.

HTTP/1.1 200 OK
Content-Length: 12
Hello world!

Model

Mojolicious 中, 我们认为 Web 应用是现有前端的简单业务逻辑, 这意味着 Mojolicious 设计完全是和 model 层无关的, 你可以使用任意的 Perl 模块来替换.

$ mkdir -p lib/MyApp/Model
$ touch lib/MyApp/Model/Users.pm
$ chmod 644 lib/MyApp/Model/Users.pm

我们的登录管理, 将只使用一个普通的原有 Perl 模块抽象出, 来匹配用户名和密码这个工作相关的逻辑. 下面的这个 MyApp::Model::Users 只是可以选择任何你所需要的东西. 只是用于分离.

package MyApp::Model::Users;

use strict;
use warnings;

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

sub new { bless {}, shift }

sub check {
  my ($self, $user, $pass) = @_;

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

  # Fail
  return undef;
}

1;

如果你想实现自己的 model 的动作和模板, 你可以使用 "helper" in Mojolicious 的功能来注册你的方法.

#!/usr/bin/env perl
use Mojolicious::Lite;

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

# /?user=sri&pass=secr3t
any '/' => sub {
  my $c = shift;

  # 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;

这个 "param" in Mojolicious::Controller 的方法会取得请求过来的参数, POST 的参数, 文件上传送的内容和 Route 的占位符取得的内容.和其它.

测试 (Testing)

Mojolicious 中我们希望大家能都认真的采用测试驱动开发, 并努力推动它.

$ mkdir t
$ touch t/login