Mojolicious::Guides::Growing-Growing Mojolicous应用程序
本文档解释了启动Mojolicious::精简从头开始制作原型并将其发展为结构良好的Mojolicious公司应用程序。本指南的最终结果也可以作为示例应用程序.
Essentials每Mojolicious公司开发人员应该知道。
MVC是一种源于Smalltalk-80的图形用户界面编程软件架构模式,用于分离应用程序逻辑、表示和输入。
+------------+ +-------+ +------+输入->|控制器|->|模型|->|视图|->输出+------------+ +-------+ +------+
模式的稍微修改版本将一些应用程序逻辑移动到控制器是当今几乎所有web框架的基础,包括Mojolicious公司.
+----------------+ +-------+请求->||<->|模型|| | +-------+|控制器|| | +-------+响应<-||<->|查看|+----------------+ +-------+
这个控制器接收来自用户的请求,将传入数据传递给模型并从中检索数据,然后由看法但请注意,此模式只是一个指导原则,大多数情况下会产生更干净、更易于维护的代码,而不是一个应该不惜一切代价遵循的规则。
REST是一种分布式超媒体系统(如web)的软件架构风格。虽然它可以应用于许多协议,但目前它最常用于HTTP。在REST术语中,当您打开类似于http://mojolicious.org/foo
使用浏览器,基本上是向web服务器请求HTML表示的http://mojolicious.org/foo
资源.
+--------+ +--------+| | -> http://mojolicious.org/foo -> | ||客户端||服务器||| Mojo摇滚乐</html><-||+--------+ +--------+
这里的基本思想是,所有资源都可以用URL唯一地寻址,每个资源可以有不同的表示形式,例如HTML、RSS或JSON。用户界面关注点与数据存储关注点相分离,所有会话状态都保留在客户端。
+---------+ +------------+||->PUT/foo->||||->你好,世界!->||| | | |||<-201创建<-||| | | |||->GET/foo->|||浏览器||Web服务器|||<-200正常<-||||<-你好,世界!<-||| | | |||->删除/foo->||| | | |||<-200正常<-||+---------+ +------------+
而HTTP方法如PUT(输出)
,GET(获取)
和删除
不是REST的直接组成部分,它们与REST配合得很好,通常用于操作资源.
HTTP被设计为无状态协议,web服务器对以前的请求一无所知,这使得用户友好的登录系统变得棘手。会话通过允许web应用程序跨多个HTTP请求保存有状态信息来解决此问题。
获取/登录?user=sebastian&pass=s3crete HTTP/1.1主持人:mojolicious.orgHTTP/1.1 200正常Set-Cookie:sessionid=987654321内容物长度:10你好,塞巴斯蒂安。GET/受保护的HTTP/1.1主持人:mojolicious.orgCookie:sessionid=987654321HTTP/1.1 200正常Set-Cookie:sessionid=987654321内容物长度:16再次问候塞巴斯蒂安。
传统上,所有会话数据都存储在服务器端,只有会话ID以cookie的形式在浏览器和web服务器之间交换。
Set-Cookie:session=hmac-sha256(base64(json($session)))
在Mojolicious公司然而,我们将这一概念向前推进了一步,将所有JSON序列化和Base64编码的内容存储在HMAC-SHA256签名cookie中,这更符合REST原理,并减少了基础设施需求。
TDD是一个软件开发过程,开发人员开始编写定义所需功能的失败测试用例,然后继续生成通过这些测试的代码。有很多优点,例如总是有良好的测试覆盖率,并且代码是为可测试性而设计的,这反过来又会防止将来的更改破坏旧代码。大部分Mojolicious公司是使用TDD开发的。
两者之间的主要区别之一Mojolicious公司其他web框架也包括Mojolicious::精简,一个针对快速原型优化的微型web框架。
你可能知道这种感觉,你有一个很酷的主意,想尽快尝试,这就是为什么Mojolicious::精简应用程序只需要一个文件。
myapp.pl#可以内联模板甚至静态文件
完全Mojolicious公司另一方面,应用程序更接近组织良好的CPAN分布,以最大限度地提高可维护性。
myapp#应用程序目录|-script#脚本目录|+-my_app#应用程序脚本|-lib#库目录||-MyApp.pm#应用程序类|+-MyApp#应用程序命名空间|+-控制器#控制器名称空间|+-Example.pm#控制器类|-my_app.yml#配置文件|-t#测试目录|+-basic.t#随机测试|-log#日志目录|+-development.log#开发模式日志文件|-public#静态文件目录(自动提供)||--assets#捆绑创建的静态资产||`--*生成的资产*|+-index.html#静态html文件+-templates#模板目录|-layouts#布局的模板目录|+-default.html.ep#布局模板+-example#“example”控制器的模板目录+-welcome.html.ep#“欢迎”动作模板
两个应用程序框架都可以使用命令自动生成Mojolicious::Command::Author::generate::lite_app和Mojolicious::Command::Author::generate::app蒙羞.
$mojo生成lite-app myapp.pl$mojo生成应用程序MyApp
就功能而言,两者几乎是相等的,唯一真正的差异是组织上的,所以每一个都可以逐渐转化为另一个。
我们用一个可执行的Perl脚本启动新的应用程序。
$mkdir我的应用程序$cd我的应用程序$touch myapp.pl$chmod 744 myapp.pl(美元)
这将是我们的登录管理器示例应用程序的基础。
#!/usr/bin/env-perl使用Mojolicious::Lite签名;获取“/”=>子($c){$c->render(text=>“你好,世界!”);};应用程序->启动;
由于自动重新加载,内置的开发web服务器使应用程序的工作变得非常有趣。
$莫博/我的应用程序.plWeb应用程序位于http://127.0.0.1:3000
只需保存更改,它们将在下次刷新浏览器时自动生效。
这一切都是从这样一个由浏览器发送的HTTP请求开始的。
获取/HTTP/1.1主机:localhost:3000
一旦web服务器通过事件循环接收到请求,它将被传递给Mojolicious公司,将通过几个简单的步骤进行处理。
检查是否存在符合要求的静态文件。
试着找到一条符合要求的路线。
将请求发送到此路由,通常会到达一个或多个操作。
处理请求,可能使用渲染器生成响应。
将控制权返回到web服务器,如果尚未生成响应,请等待非阻塞操作通过事件循环执行此操作。
使用我们的应用程序,路由器会在步骤2中找到一个操作,并在步骤4中呈现一些文本,从而将这样的HTTP响应发送回浏览器。
HTTP/1.1 200正常内容物长度:12你好,世界!
在Mojolicious公司我们认为web应用程序是现有业务逻辑的简单前端。这意味着Mojolicious公司完全是设计的模型层无关,您只需使用您最喜欢的任何Perl模块。
$mkdir-p库/MyApp/Model$touch库/MyApp/Model/Users.pm$chmod 644库/MyApp/Model/Users.pm
我们的登录管理器将使用一个普通的旧Perl模块,抽象出与匹配用户名和密码相关的所有逻辑。姓名MyApp::型号::用户
是一个任意的选择,只是用来使关注点的分离更加明显。
程序包MyApp::Model::Users;使用严格;使用警告;使用实验性qw(签名);使用Mojo::Util qw(secure_compare);我的$USERS={joel=>“las3rs”,marcus=>“lulz”,塞巴斯蒂安=>“secr3t”};sub-new($class){祝福{},$class}子检查($self、$user、$pass){#成功如果$USERS->{$user}&&secure_compare$USERS->{$user},$pass;,则返回1;#失败返回undef;}1;
可以使用函数注册一个简单的助手Mojolicious::Lite中的“helper”使我们的模型可用于所有操作和模板。
#!/usr/bin/env-perl使用Mojolicious::Lite签名;使用lib-qw(lib);使用MyApp::Model::Users;#帮助延迟初始化和存储模型对象helper-users=>sub{state$users=MyApp::Model::users->new};# /?user=sebastian&pass=secr3t任何“/”=>子($c){#查询参数my$user=$c->param('user')|'';my$pass=$c->param('pass')|'';#检查密码如果$c->users->check($user,$pass),则返回$c->render(text=>“Welcome$user.”);#失败$c->render(text=>“用户名或密码错误。”);};应用程序->启动;
方法Mojolicious::Controller中的“param”用于访问查询参数,邮政
参数、文件上传和路由占位符,全部同时进行。
在Mojolicious公司我们非常认真地对待测试,并试图让它成为一种愉快的体验。
$mkdir吨$触摸t/login.t$chmod 644吨/登录。t
测试::Mojo是一个专门为测试设计的可编写脚本的HTTP用户代理,具有许多有趣的最新功能,例如基于Mojo::DOM.
使用测试::更多;使用测试::Mojo;#包括应用程序使用Mojo::File qw(curfile);require(curfile->dirname->sibling('myapp.pl'));#允许302重定向响应my$t=测试::Mojo->new;$t->ua->max_redirects(1);#测试HTML登录表单是否存在$t->get_ok('/')->状态_ is(200)->element_exists('表单输入[名称=“用户”]')->element_exists('表单输入[名称=“pass”]')->element_exists('表单输入[类型=“提交”]');#使用有效凭据测试登录$t->post_ok('/'=>form=>{user=>'sebastian',pass=>'secr3t'})->状态_ is(200)->text_like('html body'=>qr/欢迎sebastian/);#测试访问受保护的页面$t->get_ok('/protected')->status_is(200)->text_like('a'=>qr/Logout/);#测试HTML登录表单在注销后是否再次显示$t->get_ok('/logout')->状态_ is(200)->element_exists('表单输入[名称=“用户”]')->element_exists('表单输入[名称=“pass”]')->element_exists('表单输入[类型=“提交”]');done_testing();
您的应用程序将无法通过这些测试,但从现在开始,您可以使用它们检查您的进度。
$证明-l$prove-l t/login.t证明$prove-l-v t/login.t
或直接从命令行使用执行快速请求Mojolicious::Command::get.
$ ./myapp.pl获取/用户名或密码错误。$ ./myapp.pl获取-v'/?user=sebastian&pass=secr3t'获得/?user=sebastian&pass=secr3t HTTP/1.1用户代理:Mojolicious(Perl)接受编码:gzip内容长度:0主机:localhost:59472HTTP/1.1 200正常日期:2010年7月18日,星期日13:09:58 GMT服务器:Mojolicious(Perl)内容物长度:12内容类型:文本/纯文本欢迎塞巴斯蒂安。
中的会话Mojolicious公司一旦你开始使用这个方法,基本上就是开箱即用Mojolicious::Controller中的“session”,不需要设置,但我们建议使用Mojolicious中的“秘密”.
$app->秘密(['Mojolicious rocks']);
HMAC-SHA256算法使用此密码短语使签名的cookie不受篡改,并且可以随时更改以使所有现有会话无效。
$c->会话(用户=>“sebastian”);我的$user=$c->会话(“用户”);
默认情况下,所有会话都会在一小时后过期,为了获得更多控制,您可以使用到期
session值设置从现在开始的过期日期(以秒为单位)。
$c->会话(过期=>3600);
并且可以使用到期
session值设置过去的绝对到期日期。
$c->会话(过期=>1);
对于只应在下一个请求中可见的数据,如302
使用执行重定向Mojolicious::Plugin::DefaultHelpers中的“redirect_to”,您可以使用闪光灯,通过Mojolicious::Plugin::DefaultHelpers中的“flash”.
$c->flash(消息=>“一切正常”);$c->redirect_to(“再见”);
只需记住,所有会话数据都用序列化Mojo::JSON并存储在HMAC-SHA256签名Cookie中,通常具有4096
字节(4KiB)限制,具体取决于浏览器。
决赛我的应用程序.pl
通过上述所有测试的原型可能是这样的。
#!/usr/bin/env-perl使用Mojolicious::Lite签名;使用lib-qw(lib);使用MyApp::Model::Users;#使签名的cookie具有防篡改性app->秘密(['Mojolicious rocks']);helper-users=>sub{state$users=MyApp::Model::users->new};#主登录操作任何“/”=>子($c){#查询或POST参数my$user=$c->param('user')|'';my$pass=$c->param('pass')|'';#检查密码,必要时显示“index.html.ep”返回$c->render,除非$c->users->check($user,$pass);#在会话中存储用户名$c->会话(用户=>$user);#在闪存中为下一页存储友好信息$c->flash(消息=>“感谢登录。”);#重定向到302响应的受保护页面$c->redirect_to(“受保护”);}=>“索引”;#确保用户已登录此组中的操作组{子项下($c){#如果用户未登录,则重定向至主页并返回302响应如果$c->session(“用户”),则返回1;$c->redirect_to(“索引”);返回undef;};#受保护页面自动呈现“protected.html.ep”获取“/protected”;};#注销操作获取“/logout”=>sub($c){#到期并自动清除会话$c->会话(过期=>1);#重定向到主页,返回302响应$c->redirect_to(“索引”);};应用程序->启动;__数据__@@索引.html.ep%布局“默认”;%=form_for-index=>开始%if(参数“用户”){<b>名称或密码错误,请重试</b> <br>% }姓名:<br>%=text_field“用户”<br>密码:<br>%=密码字段“pass”<br>%=submit_button“登录”%结束@@受保护的.html.ep%布局“默认”;%if(我的$msg=flash“消息”){<b><%=$msg%></b><br>% }欢迎<%=session“user”%><br>%=链接到注销=>“注销”@@布局/default.html.ep<!DOCTYPE html><html><head><title>登录管理员</title><body><%=内容%></body></html>
目录结构现在应该是这样的。
我的应用程序|-我的应用程序.pl|-图书馆|+-我的应用程序|+-型号|+-用户.pm+-t吨+-登录.t
我们的模板使用了渲染器的许多功能,Mojolicious::辅助线::渲染非常详细地解释了它们。
由于Mojolicious公司实际的生长过程有很多变化,但这应该能让你很好地了解各种可能性。
中内联的所有模板和静态文件数据
节可以自动转换为模板
和公众的
目录,使用命令Mojolicious::命令::作者::inflate.
$ ./myapp.pl充气
这些目录具有更高的优先级,因此膨胀也是允许用户自定义其应用程序的一种好方法。
这是每一个满满的心Mojolicious公司并始终在服务器启动期间实例化。
$touch库/MyApp.pm$chmod 644库/MyApp.pm
我们将首先从中提取所有操作我的应用程序.pl
并在Mojolicious::路线路由器,无需更改任何实际操作代码。
包MyApp;使用Mojo::Base“Mojolicious”,-签名;使用MyApp::Model::Users;子启动($self){$self->秘密(['Mojolicious rocks']);$self->helper(users=>sub{state$users=MyApp::Model::users->new});我的$r=$self->路线;$r->any('/'=>sub($c){my$user=$c->param('user')|'';my$pass=$c->param('pass')|'';返回$c->render,除非$c->users->check($user,$pass);$c->会话(用户=>$user);$c->flash(消息=>“感谢登录。”);$c->redirect_to(“受保护”);}=>“索引”);我的$logged_in=$r->在(sub($c)下{如果$c->session(“用户”),则返回1;$c->redirect_to(“索引”);返回undef;});$logged_in->get('/protected');$r->get('/logout'=>sub($c){$c->会话(过期=>1);$c->redirect_to(“索引”);});}1;
这个启动
方法在实例化后立即被调用,是整个应用程序设置的地方。自满起Mojolicious公司应用程序可以使用它们不需要的嵌套路由组
阻碍。
我的应用程序.pl
现在可以将其本身转换为一个简化的应用程序脚本,以允许再次运行测试。
#!/usr/bin/env-perl使用Mojo::Base-strict;使用lib-qw(lib);使用Mojolicious::Commands;#启动应用程序的命令行界面Mojolicious::Commands->start_app('MyApp');
我们的混合应用程序的目录结构应该如下所示。
我的应用程序|-我的应用程序.pl|-图书馆||-我的应用程序.pm|+-我的应用程序|+-型号|+-用户.pm|-t吨|+-登录.t+-模板|-布局|+-default.html.ep|-索引.html.ep+-受保护的.html.ep
混合路由是一个很好的中间步骤,但为了最大限度地提高可维护性,将操作代码与其路由信息分开是有意义的。
$mkdir lib/MyApp/控制器$touch库/MyApp/Controller/Login.pm$chmod 644 lib/MyApp/控制器/Login.pm
同样,实际的操作代码不需要更改,我们只需重命名$c美元
到$self(美元)
因为控制器现在是调用者。
程序包MyApp::Controller::Login;使用Mojo::Base“Mojolicious::Controller”,-签名;子索引($self){my$user=$self->param('user')|'';my$pass=$self->param('pass')|'';返回$self->render,除非$self->users->check($user,$pass);$self->session(用户=>$user);$self->flash(消息=>“感谢登录。”);$self->redirect_to(“受保护”);}sub-logged_in($self){如果$self->session('user'),则返回1;$self->redirect_to('index');返回undef;}子注销($self){$self->session(过期=>1);$self->redirect_to('index');}1;
全部Mojolicious::控制器控制器是普通的旧Perl类,可以根据需要进行实例化。
应用程序类库/我的应用程序.pm
现在可以简化为模型和路由信息。
包MyApp;使用Mojo::Base“Mojolicious”,-签名;使用MyApp::Model::Users;子启动($self){$self->秘密(['Mojolicious rocks']);$self->helper(users=>sub{state$users=MyApp::Model::users->new});我的$r=$self->路线;$r->any(“/”)->to(“登录#索引”)->name(“索引”);我的$logged_in=$r->在('/')->到('login#logged_in')下;$logged_in->get('/protected')->to('login#protected';$r->get('/logout')->to('login#logout');}1;
路由器允许多种不同的路由变化,Mojolicious::辅助线::路由非常详细地解释了它们。
模板是我们的视图,通常绑定到控制器,因此需要将其移动到适当的目录中。
$mkdir模板/登录$mv模板/index.html.ep模板/login/index.html$mv模板/protected.html.ep模板/login/protectd.html.ep
最后我的应用程序.pl
可以移动到脚本
目录并重命名为我的应用程序(_A)
遵循CPAN标准。
$mkdir脚本$mv myapp.pl脚本/my_app
只需更改一些小细节,而不是更改到图书馆我们现在使用Mojo::文件以获得绝对路径,从而允许我们从其主目录之外启动应用程序。
#!/usr/bin/env-perl使用严格;使用警告;使用Mojo::File qw(curfile);使用lib curfile->dirname->sibling('lib')->to_string;使用Mojolicious::Commands;#启动应用程序的命令行界面Mojolicious::Commands->start_app('MyApp');
完全Mojolicious公司应用程序更容易测试,所以t/login.t登录
可以简化。
使用Mojo::Base-strict;使用测试::更多;使用测试::Mojo;my$t=测试::Mojo->new(“MyApp”);$t->ua->max_redirects(1);子测试“测试登录工作流”=>子{$t->get_ok('/')->状态_ is(200)->element_exists('表单输入[名称=“用户”]')->element_exists('表单输入[名称=“pass”]')->element_exists('表单输入[类型=“提交”]');$t->post_ok('/'=>form=>{user=>'sebastian',pass=>'secr3t'})->状态_ is(200)->text_like('html body'=>qr/欢迎sebastian/);$t->get_ok('/protected')->status_is(200)->text_like('a'=>qr/Logout/);$t->get_ok('/logout')->状态_ is(200)->element_exists('表单输入[名称=“用户”]')->element_exists('表单输入[名称=“pass”]')->element_exists('表单输入[类型=“提交”]');};done_testing();
我们的最终目录结构应该如下所示。
我的应用程序|-脚本|+-我的应用程序|-图书馆||-我的应用程序.pm|+-我的应用程序||-控制器||+-登录.pm|+-型号|+-用户.pm|-t吨|+-登录.t+-模板|-布局|+-default.html.ep+-登录|-索引.html.ep+-受保护的.html.ep
测试驱动开发需要一点时间来适应,但它可能是一个非常强大的工具。
您可以继续Mojolicious::指南现在或者看看Mojolicious维基,其中包含许多不同作者编写的更多文档和示例。
如果您有任何文档可能还没有回答的问题,请毫不犹豫地在论坛,上的矩阵,或IRC公司.