Mojolicious::Lite

Mojolicious::Lite 是由 Mojolicious 做的一个实时 web 框架。

春节几天把这个框架认真学习,整理一遍。

  • Hello World

    #!/usr/bin/env perl
    
    #任何一门语言都是先hello world…..
    
    #几行语句,就可以做web容器进行使用,感觉这个框架很容易使用…不过剩下来的学习不会像hello world 这么简单了。
    
    use Mojolicious::Lite;  
    
    get '/' => sub {
    
      my $self = shift;
    
      $self->render(text => 'Hello World!');
    
    };
    
    #命令启动千万不能少,否则会报错也没有端口启动了
    
    app->start;
  • Generator

    #产生一个小型的项目,实际只是一个文件和自己写差不多
    
    mojo generate lite_app
    
    #产生一个全目录结构的Mojolicious  
    
    mojo generate app <AppName>

    眨眼研究完lite之后,找时间认真研读一下Mojolicious

  • Commands

  • 这些标准的 Mojolicious::Commands 中会提供一些常用的命令行功能.注意 CGI 和 PSGI 的环境可以自动检查出来.可以不提供参数.

    $ ./myapp.pl daemon
    
      Server available at http://127.0.0.1:3000.
    
    
    
      $ ./myapp.pl daemon -l http://*:8080
    
      Server available at http://127.0.0.1:8080.
    
    
    
      $ ./myapp.pl cgi
    
      ...CGI output...
    
    
    
      $ ./myapp.pl
    
      ...List of available commands (or automatically detected environment)...

    cgi 环境与psgi 环境目前因为psgi还没有具体理解,因此只能测试一下命令的执行情况了,

    目前担心这个启动是否稳定了,长时间。有时间压力测试一把尴尬,最好结合一个接口的访问数据库。

  • Start

  • 你可以在 app->start 的调用中加入参数,来替换掉标准的从 @ARGV 中接收参数.

    app->start('cgi');
  • Reloading

  • 如果你想你的应用在修改后能自动的 reload 的话,建议你使用 morbo 的开发用的 web 服务器,这样你就不用每次修改后重起.

    #默认3000端口
    
    $ morbo myapp.pl
    
    Server available at http://127.0.0.1:3000.

    这个命令不需要每次重新启动很适合这种perl页面的修改开发了,dancer没有这个功能感觉很不爽了。Mojo这个感觉强大一些了。

    $ morbo myapp.pl-l http://*:8080
    可以指定端口太yummy了

    大笑

  • Routes

  • Routes 只是请求过来的基本的路径。可以在路径中包含不同的占位符。$self 是 Mojolicious::Controller 对象其中包含着 HTTP request 和 HTTP response。

     use Mojolicious::Lite;
    
     # /foo
    
      get '/foo' => sub {
    
        my $self = shift;
    
        $self->render(text => 'Hello World!');
    
      };
    
    
    
      app->start;

    Mojolicious::Controller 这个类的内容很多,包含req,res,ua等很多信息,lite之后把这部分自己看一下。要学习的东西太多呀。。悲伤

  • GET/POST parameters

  • 全部的 GET 和 POST 的参数只需要通过 "param" in Mojolicious::Controller 来访问.
    #/usr/bin/perl
    
    use Mojolicious::Lite;
    
    use 5.016;
    
    # /foo?user=david&pass=123
    
    get '/foo' => sub {
    
      my $self = shift;
    
      my $user = $self->param('user');
    
      my $pass = $self->param('pass');
    
      $self->render(text => "Hello $user\t$pass");
    
    };
    
    app->start;
  • Stash and templates

  • 这个 "stash" in Mojolicious::Controller 是用来传一些数据给模板技术使用,在这我们使用的是 DATA 这个部分的模板.

    use Mojolicious::Lite;
    
    
    
      # /bar
    
      get '/bar' => sub {
    
        my $self = shift;
    
        $self->stash(one => 23);            #存放模版变量
    
        $self->render('baz', two => 24);    #渲染到baz.html.ep
    
      };
    
    
    
      app->start;
    
      __DATA__
    
    
    
      @@ baz.html.ep
    
      The magic numbers are <%= $one %> and <%= $two %>.

    模板的信息可以看看 "Embedded Perl" in Mojolicious::Guides::Rendering,要看的东西好多呀。困惑

  • HTTP

  • "req" in Mojolicious::Controller 和 "res" in Mojolicious::Controller 提供了全功能的 HTTP 的支持.

    use Mojolicious::Lite;
    
    use 5.16.2;
    
     # /agent
    
      get '/agent' => sub {
    
        my $self = shift;
    
        $self->res->headers->header('X-Bender' => 'Bite my shiny metal ass!'); #响应的头信息添加富家子段
    
        $self->render( text=> $self->req->headers->user_agent ); #useragnet
    
    
    
      };
    
    
    
      app->start;

    可以记录客户端的request的所有信息,也可以返回response增加相应的响应信息大笑

  • Route names

  • 全部的 Routes 会关联到相关的名字,会自动的取得相关的名字的模板。另外 "url_for" in Mojolicious::Controller 和 "link_to" in Mojolicious::Plugin::TagHelpers 也能帮到你。

    use Mojolicious::Lite;
    
    
    
      # /
    
      get '/' => sub {
    
        my $self = shift;
    
        $self->render;
    
      } => 'index';
    
    
    
      # /hello
    
      get '/hello';
    
    
    
      app->start;
    
      __DATA__
    
    
    
      @@ index.html.ep
    
      <%= link_to Hello  => 'hello' %>.  <!-- <a href="hello">Hello</a> -->
    
      <%= link_to Reload => 'index' %>.<!-- <a href="">Reload</a> -->
    
      @@ hello.html.ep
    
      Hello World!
    link_to 等同于href 的功能,具体使用如果要开发页面查看相应的文档帮助了。
  • Layouts

  • 模板是可以分层的,你只需要选择 helper 中的 "layout" in Mojolicious::Plugin::DefaultHelpers。给当前结果放到模板中通过 "content" in Mojolicious::Plugin::DefaultHelpers.

      use Mojolicious::Lite;
    
      #模版文件比较复杂可以放到templates目录中
    
      # /with_layout
    
      get '/with_layout' => sub {
    
        my $self = shift;
    
        $self->render('with_layout'); #也可以放到}=>'with_layout' 这样指定效果一样
    
      };
    
    
    
      app->start;
    
      __DATA__
    
    
    
    @@ with_layout.html.ep
    
      % title 'Green';
    
      % layout 'green';
    
      Hello World!
    
    
    
    @@ layouts/green.html.ep
    
      <!DOCTYPE html>
    
      <html>
    
        <head><title><%= title %></title></head>
    
        <body><%= content %></body>
    
      </html>
  • Blocks

  • 模板可以使用标准的 Perl 的功能,只需要使用 beginend 的关键字分隔.

      use Mojolicious::Lite;
    
    
    
      # /with_block
    
      get '/with_block' => 'block';
    
    
    
      app->start;
    
      __DATA__
    
    
    
      @@ block.html.ep
    
      % my $link = begin
    
        % my ($url, $name) = @_;
    
        Try <%= link_to $url => begin %><%= $name %><% end %>.
    
      % end
    
      <!DOCTYPE html>
    
      <html>
    
        <head><title>Sebastians frameworks</title></head>
    
        <body>
    
          %= $link->('http://mojolicio.us', 'Mojolicious')
    
          %= $link->('http://catalystframework.org', 'Catalyst')
    
        </body>
    
      </html>

    具体如果写页面,再去仔细研究模版里面的功能,看起来有些繁琐。

  • Captured content

  • 在 helper 中可以使用 "content_for" in Mojolicious::Plugin::DefaultHelpers 来抓到包在一个块中的内容.

      use Mojolicious::Lite;
    
    
    
      # /captured
    
      get '/captured' => sub {
    
        my $self = shift;
    
        $self->render('captured');
    
      };
    
    
    
      app->start;
    
      __DATA__
    
    
    
    @@ captured.html.ep
    
      % layout 'blue', title => 'Green';
    
      % content_for header => begin
    
        <meta http-equiv="Pragma" content="no-cache">
    
      % end
    
      Hello World!
    
      % content_for header => begin
    
        <meta http-equiv="Expires" content="-1">
    
      % end
    
    
    
    @@ layouts/blue.html.ep
    
      <!DOCTYPE html>
    
      <html>
    
        <head>
    
          <title><%= title %></title>
    
          %= content_for 'header'
    
        </head>
    
        <body><%= content %></body>
    
      </html>
  • Helpers

  • 你可以扩展内置的 Mojolicious 的这个 helpers, 全部内置的所有的原生的可以在 Mojolicious::Plugin::DefaultHelpers 和 Mojolicious::Plugin::TagHelpers 中来查看.

      use Mojolicious::Lite;
    
      # 可以将一些常用的如数据库的操作,用扩展helper的功能来实现比较精妙
    
      # "whois" helper
    
      helper whois => sub {
    
        my $self  = shift;
    
        my $agent = $self->req->headers->user_agent || 'Anonymous';
    
        my $ip    = $self->tx->remote_address;
    
        return "$agent ($ip)";
    
      };
    
    
    
      # /secret
    
      get '/secret' => sub {
    
        my $self = shift;
    
        my $user = $self->whois;
    
        $self->app->log->debug("Request from $user.");
    
      };
    
    
    
      app->start;
    
      __DATA__
    
    
    
    @@ secret.html.ep
    
      We know who you are <%= whois %>.
  • 占位符

  • 路径选择的占位符可以让你取得一些请求中的路径。结果会可以通过 "stash" in Mojolicious::Controller 和 "param" in Mojolicious::Controller 来访问.

      use Mojolicious::Lite;
    
    
    
      # /foo/test
    
      # /foo/test123
    
      get '/foo/:bar' => sub {
    
        my $self = shift;
    
        my $bar  = $self->stash('bar'); #stash方式
    
        $self->render(text => "Our :bar placeholder matched $bar");
    
      };
    
    
    
      # /testsomething/foo
    
      # /test123something/foo
    
      get '/(:bar)something/foo' => sub {
    
        my $self = shift;
    
        my $bar  = $self->param('bar'); #param方式
    
        $self->render(text => "Our :bar placeholder matched $bar");
    
      };
    
    
    
      app->start;
  • 松懈占位符

  • 松懈占位可以让你匹配直接到 /

      use Mojolicious::Lite;
    
    
    
      # /test/hello
    
      # /test123/hello
    
      # /test.123/hello
    
      get '/#you/hello' => 'groovy';
    
    
    
      app->start;
    
      __DATA__
    
    
    
      @@ groovy.html.ep
    
      Your name is <%= $you %>.
  • 通配位符

  • 配位符可以匹配到任何东西,包括 / 和 ..

      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 的方法

      use Mojolicious::Lite;
    
    
    
      # GET /hello
    
      get '/hello' => sub {
    
        my $self = shift;
    
        $self->render(text => 'Hello World!');
    
      };
    
    
    
      # PUT /hello
    
      put '/hello' => sub {
    
        my $self = shift;
    
        my $size = length $self->req->body;
    
        $self->render(text => "You uploaded $size bytes to /hello.");
    
      };
    
    
    
      # GET|POST|PATCH /bye
    
      any [qw(GET POST PATCH)] => '/bye' => sub {
    
        my $self = shift;
    
        $self->render(text => 'Bye World!');
    
      };
    
    
    
      # * /whatever
    
      any '/whatever' => sub {
    
        my $self   = shift;
    
        my $method = $self->req->method;
    
        $self->render(text => "You called /whatever with $method.");
    
      };
    
    
    
      app->start;
  • 选的占位符

  • 可选的占位符.

      use Mojolicious::Lite;
    
    
    
      # /hello
    
      # /hello/Sara
    
      get '/hello/:name' => {name => 'Sebastian'} => sub {
    
        my $self = shift;
    
        $self->render('groovy', format => 'txt');
    
      };
    
    
    
      app->start;
    
      __DATA__
    
    
    
      @@ groovy.txt.ep
    
      My name is <%= $name %>.
  • 限制性占位符

  • 有个最简单的方法,让你的占位符有严格的范围,您只需写出可能值的列表。

      use Mojolicious::Lite;
    
    
    
      # /test
    
      # /123
    
      any '/:foo' => [foo => [qw(test 123)]] => sub {
    
        my $self = shift;
    
        my $foo  = $self->param('foo');
    
        $self->render(text => "Our :foo placeholder matched $foo");
    
      };
    
    
    
      app->start;

    所有的占位符被编译到成正则表达式,所以在这也可以很容易地定制你的这个。

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

    只要确保不使用 ^ 和 $ 还有捕获组 (...), 因为这些占位符会和内置的组成一个更大的正则表达式.

  • Under


  • 认证并共享代码在多个路径之间很容易实现,只需要桥接生成的路由并使用 under 的声明。判断是认证还是路径显示只需要看看返回是否为 true 就知道了

      use Mojolicious::Lite;
    
    
    
      # Authenticate based on name parameter
    
      under sub {
    
        my $self = shift;
    
    
    
        # Authenticated
    
        my $name = $self->param('name') || '';
    
        return 1 if $name eq 'Bender';
    
    
    
        # Not authenticated
    
        $self->render('denied');
    
        return undef;
    
      };
    
    
    
      # / (with authentication)
    
      get '/' => 'index';
    
    
    
      app->start;
    
      __DATA__;
    
    
    
      @@ denied.html.ep
    
      You are not Bender, permission denied.
    
    
    
      @@ index.html.ep
    
      Hi Bender.

    要实现前缀的多个路径选择,也是一个使用的 under 的好理由。

      use Mojolicious::Lite;
    
    
    
      # /foo
    
      under '/foo';
    
    
    
      # /foo/bar
    
      get '/bar' => {text => 'foo bar'};
    
    
    
      # /foo/baz
    
      get '/baz' => {text => 'foo baz'};
    
    
    
      # /
    
      under '/' => {message => 'whatever'};
    
    
    
      # /bar
    
      get '/bar' => {inline => '<%= $message %> works'};
    
    
    
      app->start;

    你也可以使用 group 来组织相关的 routes ,这可以对多个路径进行 under 的声明.

    use Mojolicious::Lite;
    
    
    
      # Global logic shared by all routes
    
      under sub {
    
        my $self = shift;
    
        return 1 if $self->req->headers->header('X-Bender');
    
        $self->render(text => "You're not Bender.");
    
        return undef;
    
      };
    
    
    
      # Admin section
    
      group {
    
    
    
        # Local logic shared only by routes in this group
    
        under '/admin' => sub {
    
          my $self = shift;
    
          return 1 if $self->req->heaers->header('X-Awesome');
    
          $self->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;

    这个功能对于wap 根据手机号的头信息路由不同的页面不错,认证用处也很大。热烈的笑脸

  • Formats

  • 这其实是指的后缀,这个会自动根据后缀来选择.

      use Mojolicious::Lite;
    
    
    
      # /detection.html
    
      # /detection.txt
    
      get '/detection' => sub {
    
        my $self = shift;
    
        $self->render('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.

    限制性的的占位符也可以使用。

      use Mojolicious::Lite;
    
    
    
      # /hello.json
    
      # /hello.txt
    
      get '/hello' => [format => [qw(json txt)]] => sub {
    
        my $self = shift;
    
        return $self->render_json({hello => 'world'})
    
          if $self->stash('format') eq 'json';
    
        $self->render_text('hello world');
    
      };
    
    
    
      app->start;

    你也可以禁用格式检查.

      use Mojolicious::Lite;
    
    
    
      # /hello
    
      get '/hello' => [format => 0] => {text => 'No format detection.'};
    
    
    
      # Disable detection and allow the following routes selective re-enabling
    
      under [format => 0];
    
    
    
      # /foo
    
      get '/foo' => {text => 'No format detection again.'};
    
    
    
      # /bar.txt
    
      get '/bar' => [format => 'txt'] => {text => ' Just one format.'};
    
    
    
      app->start;
  • 内容协商

  • 不同的表示方法和需要真正 RESTful 的内容来协商,你可以看看 "respond_to" in Mojolicious::Controller.

      use Mojolicious::Lite;
    
    
    
      # /hello (Accept: application/json)
    
      # /hello (Accept: application/xml)
    
      # /hello.json
    
      # /hello.xml
    
      # /hello?format=json
    
      # /hello?format=xml
    
      get '/hello' => sub {
    
        my $self = shift;
    
        $self->respond_to(
    
          json => {json => {hello => 'world'}},
    
          xml  => {text => '<hello>world</hello>'},
    
          any  => {data => '', status => 204}
    
        );
    
      };
    
    
    
      app->start;

    做接口正好用的上。大笑

  • Conditions

  • 条件可以是象 agent 和 host 之前从 Mojolicious::Plugin::HeaderCondition 来的东西。可以提供强大的路由限制.

      use Mojolicious::Lite;
    
    
    
      # /foo (Firefox)
    
      get '/foo' => (agent => qr/Firefox/) => sub {
    
        my $self = shift;
    
        $self->render(text => 'Congratulations, you are using a cool browser.');
    
      };
    
    
    
      # /foo (Internet Explorer)
    
      get '/foo' => (agent => qr/Internet Explorer/) => sub {
    
        my $self = shift;
    
        $self->render(text => 'Dude, you really need to upgrade to Firefox.');
    
      };
    
    
    
      # http://mojolicio.us/bar
    
      get '/bar' => (host => 'mojolicio.us') => sub {
    
        my $self = shift;
    
        $self->render(text => 'Hello Mojolicious.');
    
      };
    
    
    
      app->start;

    控制路由跳转确实很灵活。微笑

  • Sessions


  • 签名 cookie 基于你的 session ,这个原生可以使用。直接通过 helper "session" in Mojolicious::Plugin::DefaultHelpers.

      use Mojolicious::Lite;
    
    
    
      get '/counter' => sub {
    
        my $self = shift;
    
        $self->session->{counter}++;
    
      };
    
    
    
      app->start;
    
      __DATA__
    
    
    
      @@ counter.html.ep
    
      Counter: <%= session 'counter' %>

    只需要意识到,所有的会话数据需要通过 Mojo::JSON 序列化。

  • Secret

  • 需要注意的是,你应该使用一个自定义的 "secret" in Mojolicious 来签署 Cookie 才会真正的安全。

    app->secret('My secret passphrase here');
  • File uploads

  • 所有上传的文件只要是 multipart/form-data 的请求会自动转为 Mojo::Upload 对象处理。你不用担心内存的使用,因为超过 250KB 的所有文件将被自动到一个临时文件。

      use Mojolicious::Lite;
    
    
    
      # Upload form in DATA section
    
      get '/' => 'form';
    
    
    
      # Multipart upload handler
    
      post '/upload' => sub {
    
        my $self = shift;
    
    
    
        # Check file size
    
        return $self->render(text => 'File is too big.', status => 200)
    
          if $self->req->is_limit_exceeded;
    
    
    
        # Process uploaded file
    
        return $self->redirect_to('form')
    
          unless my $example = $self->param('example');
    
        my $size = $example->size;
    
        my $name = $example->filename;
    
        $self->render(text => "Thanks for uploading $size byte file $name.");
    
      };
    
    
    
      app->start;
    
      __DATA__
    
    
    
      @@ form.html.ep
    
      <!DOCTYPE html>
    
      <html>
    
        <head><title>Upload</title></head>
    
        <body>
    
          %= form_for upload => (enctype => 'multipart/form-data') => begin
    
            %= file_field 'example'
    
            %= submit_button 'Upload'
    
          % end
    
        </body>
    
      </html>

    为了保护您避免过大的文件, 这也有一个默认极限的值 5MB。你可以使用 MOJO_MAX_MESSAGE_SIZE 的环境变量来修改这个.

      # Increase limit to 1GB
    
      $ENV{MOJO_MAX_MESSAGE_SIZE} = 1073741824;

    有些功能先知道具体的意思,具体使用到再查吧。吐舌笑脸

  • User agent

  • "ua" in Mojolicious::Controller 是一个全功能的 HTTP 和 WebSocket 的 user agent,当你和 Mojo::JSON Mojo::DOM 组合使用时非常的强大.

      use Mojolicious::Lite;
    
    
    
      get '/test' => sub {
    
        my $self = shift;
    
        $self->render(data => $self->ua->get('http://mojolicio.us')->res->body);
    
      };
    
    
    
      app->start;

    惊讶有多强大,用一用才知道了,近几天把这三部分,仔细的研究一下,趁着春节的休息。

  • WebSockets

  • WebSocket的应用程序从未如此简单。

      use Mojolicious::Lite;
    
    
    
      websocket '/echo' => sub {
    
        my $self = shift;
    
        $self->on(message => sub {
    
          my ($self, $msg) = @_;
    
          $self->send("echo: $msg");
    
        });
    
      };
    
    
    
      app->start;

    在 "message" in Mojo::Transaction::WebSocket 的事件中,我们可以对 "on" in Mojolicious::Controller 的回调进行订阅,当每次有 WebSocket 的信息时会接收到

  • 外部模板

  • 外部模板会从你的应用目录中的 templates 目录中来查找.

      use Mojolicious::Lite;
    
    
    
      # /external
    
      any '/external' => sub {
    
        my $self = shift;
    
    
    
        # templates/foo/bar.html.ep
    
        $self->render('foo/bar');
    
      };
    
    
    
      app->start;
  • 静态文件

  • 静态文件可以从 DATA 的部分(可以是 Base64 的编码)或 public 的目录查找.

      @@ something.js
    
      alert('hello!');
    
    
    
      @@ test.txt (base64)
    
      dGVzdCAxMjMKbGFsYWxh
    
    
    
      $ mkdir public
    
      $ mv something.js public/something.js
  • Testing

  • 创建一个 t 目录和进行 Perl 的单元测试,测试您的应用程序在这也一样简单。

      use Test::More;
    
      use Test::Mojo;
    
    
    
      use FindBin;
    
      require "$FindBin::Bin/../myapp.pl";
    
    
    
      my $t = Test::Mojo->new;
    
      $t->get_ok('/')->status_is(200)->content_like(qr/Funky/);
    
    
    
      done_testing();

    使用 test 命令运行全部的单元测试.

    $ ./myapp.pl test

    如果你想你的测试报告更加多的信息,你也可以直接在您的测试文件中更改该应用程序的日志级别。

    $t->app->log->level('debug');
  • 模式

  • 在生产环境我们想要禁用 debug 的信息,可以直接通过 Mojolicious 操作的命令行选项和修改 MOJO_MODE 这个环境变量,默认是使用的 development.

    $ ./myapp.pl daemon -m production

    这也影响到模板其它的地方,如 exceptionnot_found 的模板.

  • Logging

  • 使用 Mojo::Log 时信息会自动的写到标准错误输入和 log/$mode.log 的文件中,当然必须 log 目录存在.

    更多的控制可以直接访问 Mojolicious 的对象.

      use Mojolicious::Lite;
    
    
    
      app->log->level('error');
    
      app->routes->get('/foo/:bar' => sub {
    
        my $self = shift;
    
        $self->app->log->debug('Got a request for "Hello Mojo!".');
    
        $self->render(text => 'Hello Mojo!');
    
      });
    
    
    
      app->start;

    你可能感兴趣的:(IO)