Mojolicious::Controller

简介

# Controller
package MyApp::Controller::Foo;
use Mojo::Base 'Mojolicious::Controller';

# Action
sub bar {
  my $self = shift;
  my $name = $self->param('name');
  $self->res->headers->cache_control('max-age=1, no-cache');
  $self->render(json => {hello => $name});
}

Mojolicious::Controller是Mojolicious应用程序中控制器的基类。如果你没有在Mojolicious中设置“controller_class”,Mojolicious::Controller将被作为默认的控制器类。

属性

Mojolicious::Controller从Mojo::Base继承所有属性,并实现以下属性。

app

my $app = $c->app;
$c      = $c->app(Mojolicious->new);

反向引用分配给该控制器的应用程序,通常是Mojolicious对象。

# Use application logger
$c->app->log->debug('Hello Mojo');

# Generate path
my $path = $c->app->home->child('templates', 'foo', 'bar.html.ep');

match

my $m = $c->match;
$c    = $c->match(Mojolicious::Routes::Match->new);

当前请求的路由结果,默认为Mojo::Routes::Match对象。

# Introspect
my $name   = $c->match->endpoint->name;
my $foo    = $c->match->endpoint->pattern->defaults->{foo};
my $action = $c->match->stack->[-1]{action};

tx

my $tx = $c->tx;
$c     = $c->tx(Mojo::Transaction::HTTP->new);

正在处理的事务,通常是Mojo::Transaction::HTTP或Mojo::Transaction::WebSocket对象。

注:此引用通常会用被消弱,因此,当你执行非阻塞操作并且底层连接可能会提前关闭时,该对象需要在别处引用。

# Check peer information
my $address = $c->tx->remote_address;
my $port    = $c->tx->remote_port;

# Increase size limit for WebSocket messages to 16MiB
$c->tx->max_websocket_size(16777216) if $c->tx->is_websocket;

# Perform non-blocking operation without knowing the connection status
my $tx = $c->tx;
Mojo::IOLoop->timer(2 => sub {
  $c->app->log->debug($tx->is_finished ? 'Finished' : 'In progress');
});

方法

Mojolicious::Controller继承Mojo::Base中的所有方法,并实现以下方法。

continue

$c->continue;

使用Mojolicious::Routes中的continue方法从 已经包装的“调度器”链中继续调度。

cookie

my $value = $c->cookie('foo');
$c        = $c->cookie(foo => 'bar');
$c        = $c->cookie(foo => 'bar', {path => '/'});

获取一个“请求”中的cookie,或设置一个“响应”中的cookie。如果有多个cookie值共享同一个名称,使用这个方法只能获取到最后一个,如果你想获取到所有的值可以使用every_cookie方法。

# Create response cookie with domain and expiration date
$c->cookie(user => 'sri', {domain => 'example.com', expires => time + 60});

# Create secure response cookie
$c->cookie(secret => 'I <3 Mojolicious', {secure => 1, httponly => 1});

every_cookie

my $values = $c->every_cookie('foo');

和cookie方法的功能类似,但返回的是一个包含所有相同名称所有cookie值的数组引用。

$ Get first cookie value
my $first = $c->every_cookie('foo')->[0];

every_param

my $values = $c->every_param('foo');

和param方法的功能类似,但返回的是一个包含所有相同名称所有param值的数组引用。

# Get first value
my $first = $c->every_param('foo')->[0];

every_signed_cookie

my $values = $c->every_signed_cookie('foo');

和signed_cookie方法的功能类似,但返回的是一个包含相同名称所有已签名cookie值的数组引用。

# Get first signed cookie value
my $first = $c->every_signed_cookie('foo')->[0];

finish

$c = $c->finish;
$c = $c->finish(1000);
$c = $c->finish(1003 => 'Cannot accept data!');
$c = $c->finish('Bye!');

关闭WebSocket连接或长时间轮询流,此方法将自动响应具有101响应状态的WebSocket握手请求,以建立WebSocket连接。

flash

my $foo = $c->flash('foo');
$c      = $c->flash({foo => 'bar'});
$c      = $c->flash(foo => 'bar');

只能在下一次请求中可用的持久存储,会被存储在session中。

# Show message after redirect
$c->flash(message => 'User created successfully!');
$c->redirect_to('show_user', id => 23);

helpers

my $helpers = $c->helpers;

返回包含当前控制器对的代理对象,可以使用他调用由app提供的helpers。这包括Mojolicious::Plugin::DefaultHelpers和Mojolicious::Plugin::TagHelpers中的所有helpers。

# Make sure to use the "title" helper and not the controller method
$c->helpers->title('Welcome!');

# Use a nested helper instead of the "reply" controller method
$c->helpers->reply->not_found;

on

my $cb = $c->on(finish => sub {...});

订阅tx的事件,通常是Mojo::Transaction::HTTP或Mojo::Transaction::WebSocket对象。此方法将自动响应具有101响应状态的WebSocket握手请求,以建立WebSocket连接。

# Do something after the transaction has been finished
$c->on(finish => sub {
  my $c = shift;
  $c->app->log->debug('All data has been sent');
});

# Receive WebSocket message
$c->on(message => sub {
  my ($c, $msg) = @_;
  $c->app->log->debug("Message: $msg");
});

# Receive JSON object via WebSocket message
$c->on(json => sub {
  my ($c, $hash) = @_;
  $c->app->log->debug("Test: $hash->{test}");
});

# Receive WebSocket "Binary" message
$c->on(binary => sub {
  my ($c, $bytes) = @_;
  my $len = length $bytes;
  $c->app->log->debug("Received $len bytes");
});

param

my $value = $c->param('foo');
$c        = $c->param(foo => 'ba;r');
$c        = $c->param(foo => 'ba;r', 'baz');
$c        = $c->param(foo => ['ba;r', 'baz']);

顺序访问路径占位符没有被隐藏的值,文件上传,GET和POST请求中使用application/x-www-form-urlencoded 或 multipart/form-data 格式编码的消息体。如果有多个值共享相同的名称,使用此方法只能得到众值中的最后一个值。如果你想访问所有的值,则需要使用every_param方法。

注:“请求体”每个Parts 都需要加载到内存中被解析,所以需要确保它不会太大。默认情况下限制为16MiB。

# Get first value
my $first = $c->every_param('foo')->[0];

为了更精确和方便的访问,您可以使用以下类似方式直接访问请求信息。

# Only GET parameters
my $foo = $c->req->query_params->param('foo');

# Only POST parameters
my $foo = $c->req->body_params->param('foo');

# Only GET and POST parameters
my $foo = $c->req->param('foo');

# Only file uploads
my $foo = $c->req->upload('foo');

redirect_to

$c = $c->redirect_to('named', foo => 'bar');
$c = $c->redirect_to('named', {foo => 'bar'});
$c = $c->redirect_to('/index.html');
$c = $c->redirect_to('http://example.com/index.html');

准备一个302(如果当前状态吗不是3xx)状态码,使用Location头的重定向响应,参数要求与url_for方法相同。

# Moved Permanently
$c->res->code(301);
$c->redirect_to('some_route');

# Temporary Redirect
$c->res->code(307);
$c->redirect_to('some_route');

render

my $bool = $c->render;
my $bool = $c->render(foo => 'bar', baz => 23);
my $bool = $c->render(template => 'foo/index');
my $bool = $c->render(template => 'index', format => 'html');
my $bool = $c->render(data => $bytes);
my $bool = $c->render(text => 'Hello!');
my $bool = $c->render(json => {foo => 'bar'});
my $bool = $c->render(handler => 'something');
my $bool = $c->render('foo/index');

使用Mojolicious中的renderer渲染内容,并在Mojolicious中先后触发before_render和after_render事件。接收的参数必须为键值对,它们会被合并到stash中。如果无法产生一个响应,则会调用Mojolicious::Plugin::DefaultHelpers中的reply->not_found 方法。

# Render characters
$c->render(text => 'I ? Mojolicious!');

# Render characters (alternative)
$c->stash(text => 'I ? Mojolicious!')->render;

# Render binary data
use Mojo::JSON 'encode_json';
$c->render(data => encode_json({test => 'I ? Mojolicious!'}));

# Render JSON
$c->render(json => {test => 'I ? Mojolicious!'});

# Render inline template
$c->render(inline => '<%= 1 + 1 %>');
 
# Render template "foo/bar.html.ep"
$c->render(template => 'foo/bar', format => 'html', handler => 'ep');

# Render template "test.*.*" with arbitrary values "foo" and "bar"
$c->render(template => 'test', foo => 'test', bar => 23);

# Render template "test.xml.*"
$c->render(template => 'test', format => 'xml');
 
# Render template "test.xml.*" (alternative)
$c->render('test', format => 'xml');

render_later

$c = $c->render_later;

禁用自动渲染以延迟响应生成,只有在自动渲染会导致发送“响应”时才需要。

# Delayed rendering
$c->render_later;
Mojo::IOLoop->timer(2 => sub {
  $c->render(text => 'Delayed by 2 seconds!');
});

render_maybe

my $bool = $c->render_maybe;
my $bool = $c->render_maybe(foo => 'bar', baz => 23);
my $bool = $c->render_maybe('foo/index', format => 'html');

尝试呈现内容,如果无法产生一个响应,也不会调用Mojolicious::Plugin::DefaultHelpers中的reply->not_found 方法。并且使用与render完全相同的参数。

# Render template "index_local" only if it exists
$c->render_maybe('index_local') or $c->render('index');

render_to_string

my $output = $c->render_to_string('foo/index', format => 'pdf');

尝试渲染内容并将生成的内容包装成Mojo::BtyeStream对象返回,如果渲染失败,则返回undef。所有参数都将自动从本地获取,并且仅在此渲染操作期间可用。使用与“render”相同的参数。

# Render inline template
my $two = $c->render_to_string(inline => '<%= 1 + 1 %>');

rendered

$c = $c->rendered;
$c = $c->rendered(302);

在Mojolicious中完成响应,并触发after_dispatch的hook。默认使用200响应代码。

# Custom response
$c->res->headers->content_type('text/plain');
$c->res->body('Hello World!');
$c->rendered(200);

req

my $req = $c->req;

从tx中获取Mojo::Message::Request对象。

# Longer version
my $req = $c->tx->req;

# Extract request information
my $method = $c->req->method;
my $url    = $c->req->url->to_abs;
my $info   = $c->req->url->to_abs->userinfo;
my $host   = $c->req->url->to_abs->host;
my $agent  = $c->req->headers->user_agent;
my $custom = $c->req->headers->header('Custom-Header');
my $bytes  = $c->req->body;
my $str    = $c->req->text;
my $hash   = $c->req->params->to_hash;
my $all    = $c->req->uploads;
my $value  = $c->req->json;
my $foo    = $c->req->json('/23/foo');
my $dom    = $c->req->dom;
my $bar    = $c->req->dom('div.bar')->first->text;

res

my $res = $c->res;

从tx中获取Mojo::Message::Response对象。

# Longer version
my $res = $c->tx->res;

# Force file download by setting a response header
$c->res->headers->content_disposition('attachment; filename=foo.png;');

# Use a custom response header
$c->res->headers->header('Custom-Header' => 'whatever');

# Make sure response is cached correctly
$c->res->headers->cache_control('public, max-age=300');
$c->res->headers->append(Vary => 'Accept-Encoding');

respond_to

$c = $c->respond_to(
  json => {json => {message => 'Welcome!'}},
  html => {template => 'welcome'},
  any  => sub {...}
);

从Accept头、format存储或 GET/POST中的format参数中自动的选择最佳的内容响应格式。默认为Mojolicious::Renderer中的default_format或呈现空的204响应。

每种“内容类型”都可以使用一个回调函数或包含要传递给render方法的参数的哈希引用来处理。由于浏览器不会真正知道他们实际想要的是什么,所以在允许多种MIME类型的非特定的请求头Accept会被忽略,除非你使用X-Requested-With请求头在XMLHttpRequest类型的请求中进行了说明。

# Everything else than "json" and "xml" gets a 204 response
$c->respond_to(
  json => sub { $c->render(json => {just => 'works'}) },
  xml  => {text => 'works'},
  any  => {data => '', status => 204}
);

对于更高级的内容协商逻辑,你可以使用Mojolicious::Plubin::DefaultHelpers中的accept方法来实现。

send

$c = $c->send({binary => $bytes});
$c = $c->send({text   => $bytes});
$c = $c->send({json   => {test => [1, 2, 3]}});
$c = $c->send([$fin, $rsv1, $rsv2, $rsv3, $op, $payload]);
$c = $c->send($chars);
$c = $c->send($chars => sub {...});

通过WebSocket非阻塞的发送消息或WebSocket帧。如果传递了一个回调函数,则在所有数据定入后会被执行。此方法将自动响应具有101响应状态的WebSocket握手请求,以建立WebSocket连接。

# Send "Text" message
$c->send('I ? Mojolicious!');

# Send JSON object as "Text" message
$c->send({json => {test => 'I ? Mojolicious!'}});

# Send JSON object as "Binary" message
use Mojo::JSON 'encode_json';
$c->send({binary => encode_json({test => 'I ? Mojolicious!'})});

# Send "Ping" frame
use Mojo::WebSocket 'WS_PING';
$c->send([1, 0, 0, 0, WS_PING, 'Hello World!']);

# Make sure the first message has been written before continuing
$c->send('First message!' => sub {
  my $c = shift;
  $c->send('Second message!');
});

对于大多数空闲的WebSocket,您可能还需要在Mojolicious::Plubin::DefaultHelpers中设置inactiveivity_timeout(链接的在不活动时的等待时间),通常默认为15秒。

# Increase inactivity timeout for connection to 300 seconds
$c->inactivity_timeout(300);

session

my $session = $c->session;
my $foo     = $c->session('foo');
$c          = $c->session({foo => 'bar'});
$c          = $c->session(foo => 'bar');

获取或替换session中的数据。所有会话数据通过Mojo::JSON进行序列化,然后后进行Base64编码,再执行HMAC-SHA1进行签名,最后把签名结果放到cookie中,以防止篡改。cookie通常有一个4096字节(4KiB)的限制,具体取决于浏览器。

# Manipulate session
$c->session->{foo} = 'bar';
my $foo = $c->session->{foo};
delete $c->session->{foo};

# Expiration date in seconds from now (persists between requests)
$c->session(expiration => 604800);

# Expiration date as absolute epoch time (only valid for one request)
$c->session(expires => time + 604800);

# Delete whole session by setting an expiration date in the past
$c->session(expires => 1);

signed_cookie

my $value = $c->signed_cookie('foo');
$c        = $c->signed_cookie(foo => 'bar');
$c        = $c->signed_cookie(foo => 'bar', {path => '/'});

从“请求”中获取已经签名的cookie,或向“响应”中设置签名的cookie。如果有多个值共享一个名称,你将从这个方法中得到众值中的最后一个。你可以使用every_signed_cookie获取所有值。

Cookies使用HMAC-SHA1加密签名,以防止篡改,并且将会自动丢弃签名验证失败的cookie。

stash

my $hash = $c->stash;
my $foo  = $c->stash('foo');
$c       = $c->stash({foo => 'bar', baz => 23});
$c       = $c->stash(foo => 'bar', baz => 23);

获取或设置为当前请求存储的非持久的数据,只能在当前请求中使用。可以使用Mojolicious中的defaults方法设置默认值。有些值具有特殊含义在此列出:action,app,cb,controller,data,extends,format,handler,inline,json,layout,namespace,path,status,template,text和variant。

注:具有mojo.*前缀的所有存储空间都保留供Mojolicious框架内部使用。

# Remove value
my $foo = delete $c->stash->{foo};

# Assign multiple values at once
$c->stash(foo => 'test', bar => 23);

url_for

my $url = $c->url_for;
my $url = $c->url_for(name => 'sebastian');
my $url = $c->url_for({name => 'sebastian'});
my $url = $c->url_for('test', name => 'sebastian');
my $url = $c->url_for('test', {name => 'sebastian'});
my $url = $c->url_for('/index.html');
my $url = $c->url_for('//example.com/index.html');
my $url = $c->url_for('http://example.com/index.html');
my $url = $c->url_for('mailto:[email protected]');
my $url = $c->url_for('#whatever');

使用当前应用程序的信息(如:base、path、URL、route)生成一个便携的Mojo::URL对象。

# "http://127.0.0.1:3000/index.html" if application was started with Morbo
$c->url_for('/index.html')->to_abs;

# "https://127.0.0.1:443/index.html" if application was started with Morbo
$c->url_for('/index.html')->to_abs->scheme('https')->port(443);

# "/index.html?foo=bar" if application is deployed under "/"
$c->url_for('/index.html')->query(foo => 'bar');

# "/myapp/index.html?foo=bar" if application is deployed under "/myapp"
$c->url_for('/index.html')->query(foo => 'bar');

你还可以使用Mojolicious::Plugin::DefaultHelpers中的url_with从当前请求继承查询参数。

# "/list?q=mojo&page=2" if current request was for "/list?q=mojo&page=1"
$c->url_with->query([page => 2]);

validation

my $validation = $c->validation;

获取 Mojolicious::Validator::Validation对象,用于验证当前请求中的一些信息。如:文件上传,GET或POST中的参数等。

注:“请求体”每个Parts 都需要加载到内存中被解析,所以需要确保它不会太大。默认情况下限制为16MiB。

# Validate GET/POST parameter
my $validation = $c->validation;
$validation->required('title', 'trim')->size(3, 50);
my $title = $validation->param('title');

# Validate file upload
my $validation = $c->validation;
$validation->required('tarball')->upload->size(1, 1048576);
my $tarball = $validation->param('tarball');

write

$c = $c->write;
$c = $c->write('');
$c = $c->write($bytes);
$c = $c->write($bytes => sub {...});

非阻塞地向响应中写入动态内容,如果参数中有回调函数,将在内容写完后执行。如果在调用此方法时没有传入一个数据库,则会处理并完成响应头,且允许稍候再写入动态内容。

# Keep connection alive (with Content-Length header)
$c->res->headers->content_length(6);
$c->write('Hel' => sub {
  my $c = shift;
  $c->write('lo!');
});

# Close connection when finished (without Content-Length header)
$c->write('Hel' => sub {
  my $c = shift;
  $c->write('lo!' => sub {
    my $c = shift;
    $c->finish;
  });
});

你可以随时调用finish方法或写入空的数据空来结束响应流。

HTTP/1.1 200 OK
Date: Sat, 13 Sep 2014 16:48:29 GMT
Content-Length: 6
Server: Mojolicious (Perl)

Hello!

HTTP/1.1 200 OK
Connection: close
Date: Sat, 13 Sep 2014 16:48:29 GMT
Server: Mojolicious (Perl)

Hello!

对于长轮询,你还需要通过Mojolicious::Plugin::DefaultHelpers中的inactivity_timeout来增加连接在非活动状态的保持时间,默认值为15秒。

# Increase inactivity timeout for connection to 300 seconds
$c->inactivity_timeout(300);

write_chunk

$c = $c->write_chunk;
$c = $c->write_chunk('');
$c = $c->write_chunk($bytes);
$c = $c->write_chunk($bytes => sub {...});

使用分场传输的编码方式非阻塞的向响应中写入动态内容。如果参数中有回调函数,将在内容写完后执行。如果在调用此方法时没有传入一个数据库,则会处理并完成响应头,且允许稍候再写入动态内容。

# Make sure previous chunk has been written before continuing
$c->write_chunk('H' => sub {
  my $c = shift;
  $c->write_chunk('ell' => sub {
    my $c = shift;
    $c->finish('o!');
  });
});

你可以随时调用finish方法或写入空的数据空来结束响应流。

HTTP/1.1 200 OK
Date: Sat, 13 Sep 2014 16:48:29 GMT
Transfer-Encoding: chunked
Server: Mojolicious (Perl)

1
H
3
ell
2
o!
0

AUTOLOAD

除了上述的属性和方法外,你还可以在Mojolicious::Controller对象上调用由app提供的helpers。这包括Mojolicious::Plugin::DefaultHelpers和Mojolicious::Plugin::TagHelpers中的所有helpers。

# Call helpers
$c->layout('green');
$c->title('Welcome!');

# Longer version
$c->helpers->layout('green');

你可能感兴趣的:(Mojolicious::Controller)