天天看點

dancer cookbook 小議3

毀壞一個會話

When you're done with your session, you can destroy it:

session->destroy 

Sessions and logging in ------->會話和登陸

A common requirement is to check the user is logged in, and, if not, require them to log in before continuing.

This can easily be handled with a before hook to check their session:

hook 'before' => sub {

    if (! session('user') && request->path_info !~ m{^/login}) {

var requested_path => request->path_info;

request->path_info('/login');

    }

}; 

get '/login' => sub {

    # Display a login page; the original URL they requested is available as    

# vars->{requested_path}, so could be put in a hidden field in the form    template 'login', { path => vars->{requested_path} };

post '/login' => sub {

    # Validate the username and password they supplied

    if (params->{user} eq 'bob' && params->{pass} eq 'letmein') {

session user => params->{user};

        redirect params->{path} || '/';

} else {

redirect '/login?failed=1';

}

In your login page template, you'll want a text field named user, a password field named pass, and a hidden field named path, which will be populated with the path originally requested, so that it's sent back in the POST submission, and can be used by the post route to redirect onwards to the page originally requested once you're logged in.

Of course, you'll probably want to validate your users against a database table, or maybe via IMAP/LDAP/SSH/POP3/local system accounts via PAM etc. Authen::Simple is probably a good starting point here!

A simple working example of handling authentication against a database table yourself (using Dancer::Plugin::Database which provides the database keyword, and Crypt::SaltedHash to handle salted hashed passwords (well, you wouldn't store your users passwords in the clear, would you?)) follows:

    my $user = database->quick_select('users',

{ username => params->{user} }

);

    if (!$user) {

warning "Failed login for unrecognised user " . params->{user};        redirect '/login?failed=1';

if (Crypt::SaltedHash->validate($user->{password}, params->{pass}))

        {

debug "Password correct";

            # Logged in successfully

            session user => $user; 

redirect params->{path} || '/'; 

debug("Login failed - password incorrect for " . params->{user});      redirect '/login?failed=1'; 

Retrieve complete hash stored in session

Get complete hash stored in session:

my $hash = session; 

APPEARANCE------->頁面顯示

Using templates - views and layouts

Returning plain content is all well and good for examples or trivial apps, but soon you'll want to use templates to maintain separation between your code and your content. Dancer makes this easy.

Your route handlers can use the template keyword to render templates.

Views

It's possible to render the action's content with a template, this is called a view. The 'appdir/views' directory is the place where views are located.

You can change this location by changing the setting 'views'.

通過views設定來改變視圖存儲地

By default, the internal template engine Dancer::Template::Simple is used, but you may want to upgrade to Template::Toolkit. If you do so, you have to enable this engine in your settings as explained in Dancer::Template::TemplateToolkit. If you do so, you'll also have to import the Template module in your application code.

Note that, by default, Dancer configures the Template::Toolkit engine to use <% %> brackets instead of its default [% %] brackets. You can change this by using the following in your config file:

template: template_toolkit

engines:

    template_toolkit:

start_tag: '[%'

        stop_tag: '%]' 

All views must have a '.tt' extension. This may change in the future.

In order to render a view, just call the template|Dancer/template keyword at the end of the action by giving the view name and the HASHREF of tokens to interpolate in the view (note that for convenience, the request, session, params and vars are automatically accessible in the view, named request, session, params and vars) - for example:

hook 'before' => sub { var time => scalar(localtime) };

get '/hello/:name' => sub {

    my $name = params->{name};

    template 'hello.tt', { name => $name };

The template 'hello.tt' could contain, for example:

<p>Hi there, <% name %>!</p>

<p>You're using <% request.user_agent %></p>

<% IF session.username %>

    <p>You're logged in as <% session.username %>

<% END %>

It's currently <% vars.time %> 

For a full list of the tokens automatically added to your template (like session, request and vars, refer to Dancer::Template::Abstract).

Layouts

A layout is a special view, located in the 'layouts' directory (inside the views directory) which must have a token named 'content'. That token marks the place where to render the action view. This lets you define a global layout for your actions, and have each individual view contain only the specific content. This is a good thing to avoid lots of needless duplication of HTML :)

Here is an example of a layout: views/layouts/main.tt :

<html>

    <head>...</head>

    <body>    

<div id="header">

    ...

    </div>

<div id="content"> 

<% content %>

    </div> 

    </body>

</html> 

You can tell your app which layout to use with layout: name in the config file, or within your code:

set layout => 'main'; 

You can control which layout to use (or whether to use a layout at all) for a specific request without altering the layout setting by passing an options hashref as the third param to the template keyword:

template 'index.tt', {}, { layout => undef }; 

If your application is not mounted under root (/), you can use a before_template_render hook instead of hardcoding the path to your application for your css, p_w_picpaths and javascript:

hook 'before_template_render' => sub {

    my $tokens = shift;

    $tokens->{uri_base} = request->base->path;

繼續閱讀