3 Catalyst::Manual::Tutorial::Authentication - Catalyst Tutorial - Part 4: Authentication
8 This is B<Part 4 of 9> for the Catalyst tutorial.
10 L<Tutorial Overview|Catalyst::Manual::Tutorial>
16 L<Introduction|Catalyst::Manual::Tutorial::Intro>
20 L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
24 L<Basic CRUD|Catalyst::Manual::Tutorial::BasicCRUD>
32 L<Authorization|Catalyst::Manual::Tutorial::Authorization>
36 L<Debugging|Catalyst::Manual::Tutorial::Debugging>
40 L<Testing|Catalyst::Manual::Tutorial::Testing>
44 L<AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
48 L<Appendices|Catalyst::Manual::Tutorial::Appendices>
55 Since this tutorial was written, there has been a new Authentication
56 API released (Catalyst::Plugin::Authentication version 0.1 and later).
57 Some of this tutorial does not work with this API, and requires
58 minimal changes. For an example application that uses the new API see
59 L<http://dev.catalyst.perl.org/repos/Catalyst/trunk/examples/NewAuthApp/NewAuthApp-0.01.tar.gz>. It
60 is recommended that you read this tutorial first, and then download
61 the source code linked above to understand the differences.
65 Now that we finally have a simple yet functional application, we can
66 focus on providing authentication (with authorization coming next in
69 This part of the tutorial is divided into two main sections: 1) basic,
70 cleartext authentication and 2) hash-based authentication.
72 You can checkout the source code for this example from the catalyst
73 subversion repository as per the instructions in
74 L<Catalyst::Manual::Tutorial::Intro>
76 =head1 BASIC AUTHENTICATION
78 This section explores how to add authentication logic to a Catalyst
82 =head2 Add Users and Roles to the Database
84 First, we add both user and role information to the database (we will
85 add the role information here although it will not be used until the
86 authorization section, Part 5). Create a new SQL script file by opening
87 C<myapp02.sql> in your editor and insert:
90 -- Add users and roles tables, along with a many-to-many join table
93 id INTEGER PRIMARY KEY,
102 id INTEGER PRIMARY KEY,
105 CREATE TABLE user_roles (
108 PRIMARY KEY (user_id, role_id)
111 -- Load up some initial test data
113 INSERT INTO users VALUES (1, 'test01', 'mypass', 't01@na.com', 'Joe', 'Blow', 1);
114 INSERT INTO users VALUES (2, 'test02', 'mypass', 't02@na.com', 'Jane', 'Doe', 1);
115 INSERT INTO users VALUES (3, 'test03', 'mypass', 't03@na.com', 'No', 'Go', 0);
116 INSERT INTO roles VALUES (1, 'user');
117 INSERT INTO roles VALUES (2, 'admin');
118 INSERT INTO user_roles VALUES (1, 1);
119 INSERT INTO user_roles VALUES (1, 2);
120 INSERT INTO user_roles VALUES (2, 1);
121 INSERT INTO user_roles VALUES (3, 1);
123 Then load this into the C<myapp.db> database with the following command:
125 $ sqlite3 myapp.db < myapp02.sql
128 =head2 Add User and Role Information to DBIC Schema
130 This step adds DBIC-based classes for the user-related database tables
131 (the role information will not be used until Part 5):
133 Edit C<lib/MyAppDB.pm> and update the contents to match (only the
134 C<MyAppDB =E<gt> [qw/Book BookAuthor Author User UserRole Role/]> line
141 MyAppDB -- DBIC Schema Class
145 # Our schema needs to inherit from 'DBIx::Class::Schema'
146 use base qw/DBIx::Class::Schema/;
148 # Need to load the DB Model classes here.
149 # You can use this syntax if you want:
150 # __PACKAGE__->load_classes(qw/Book BookAuthor Author User UserRole Role/);
151 # Also, if you simply want to load all of the classes in a directory
152 # of the same name as your schema class (as we do here) you can use:
153 # __PACKAGE__->load_classes(qw//);
154 # But the variation below is more flexible in that it can be used to
155 # load from multiple namespaces.
156 __PACKAGE__->load_classes({
157 MyAppDB => [qw/Book BookAuthor Author User UserRole Role/]
163 =head2 Create New "Result Source Objects"
165 Create the following three files with the content shown below.
167 C<lib/MyAppDB/User.pm>:
169 package MyAppDB::User;
171 use base qw/DBIx::Class/;
173 # Load required DBIC stuff
174 __PACKAGE__->load_components(qw/PK::Auto Core/);
176 __PACKAGE__->table('users');
177 # Set columns in table
178 __PACKAGE__->add_columns(qw/id username password email_address first_name last_name/);
179 # Set the primary key for the table
180 __PACKAGE__->set_primary_key('id');
188 # 1) Name of relationship, DBIC will create accessor with this name
189 # 2) Name of the model class referenced by this relationship
190 # 3) Column name in *foreign* table
191 __PACKAGE__->has_many(map_user_role => 'MyAppDB::UserRole', 'user_id');
196 MyAppDB::User - A model object representing a person with access to the system.
200 This is an object that represents a row in the 'users' table of your application
201 database. It uses DBIx::Class (aka, DBIC) to do ORM.
203 For Catalyst, this is designed to be used through MyApp::Model::MyAppDB.
204 Offline utilities may wish to use this class directly.
211 C<lib/MyAppDB/Role.pm>:
213 package MyAppDB::Role;
215 use base qw/DBIx::Class/;
217 # Load required DBIC stuff
218 __PACKAGE__->load_components(qw/PK::Auto Core/);
220 __PACKAGE__->table('roles');
221 # Set columns in table
222 __PACKAGE__->add_columns(qw/id role/);
223 # Set the primary key for the table
224 __PACKAGE__->set_primary_key('id');
232 # 1) Name of relationship, DBIC will create accessor with this name
233 # 2) Name of the model class referenced by this relationship
234 # 3) Column name in *foreign* table
235 __PACKAGE__->has_many(map_user_role => 'MyAppDB::UserRole', 'role_id');
240 MyAppDB::Role - A model object representing a class of access permissions to
245 This is an object that represents a row in the 'roles' table of your
246 application database. It uses DBIx::Class (aka, DBIC) to do ORM.
248 For Catalyst, this is designed to be used through MyApp::Model::MyAppDB.
249 "Offline" utilities may wish to use this class directly.
256 C<lib/MyAppDB/UserRole.pm>:
258 package MyAppDB::UserRole;
260 use base qw/DBIx::Class/;
262 # Load required DBIC stuff
263 __PACKAGE__->load_components(qw/PK::Auto Core/);
265 __PACKAGE__->table('user_roles');
266 # Set columns in table
267 __PACKAGE__->add_columns(qw/user_id role_id/);
268 # Set the primary key for the table
269 __PACKAGE__->set_primary_key(qw/user_id role_id/);
277 # 1) Name of relationship, DBIC will create accessor with this name
278 # 2) Name of the model class referenced by this relationship
279 # 3) Column name in *this* table
280 __PACKAGE__->belongs_to(user => 'MyAppDB::User', 'user_id');
284 # 1) Name of relationship, DBIC will create accessor with this name
285 # 2) Name of the model class referenced by this relationship
286 # 3) Column name in *this* table
287 __PACKAGE__->belongs_to(role => 'MyAppDB::Role', 'role_id');
292 MyAppDB::UserRole - A model object representing the JOIN between Users and Roles.
296 This is an object that represents a row in the 'user_roles' table of your application
297 database. It uses DBIx::Class (aka, DBIC) to do ORM.
299 You probably won't need to use this class directly -- it will be automatically
300 used by DBIC where joins are needed.
302 For Catalyst, this is designed to be used through MyApp::Model::MyAppDB.
303 Offline utilities may wish to use this class directly.
309 The code for these three result source classes is obviously very familiar to the C<Book>, C<Author>, and C<BookAuthor> classes created in Part 2.
312 =head2 Sanity-Check Reload of Development Server
314 We aren't ready to try out the authentication just yet; we only want to do a quick check to be sure our model loads correctly. Press C<Ctrl-C> to kill the previous server instance (if it's still running) and restart it:
316 $ script/myapp_server.pl
318 Look for the three new model objects in the startup debug output:
321 .-------------------------------------------------------------------+----------.
323 +-------------------------------------------------------------------+----------+
324 | MyApp::Controller::Books | instance |
325 | MyApp::Controller::Root | instance |
326 | MyApp::Model::MyAppDB | instance |
327 | MyApp::Model::MyAppDB::Author | class |
328 | MyApp::Model::MyAppDB::Book | class |
329 | MyApp::Model::MyAppDB::BookAuthor | class |
330 | MyApp::Model::MyAppDB::Role | class |
331 | MyApp::Model::MyAppDB::User | class |
332 | MyApp::Model::MyAppDB::UserRole | class |
333 | MyApp::View::TT | instance |
334 '-------------------------------------------------------------------+----------'
337 Again, notice that your "result source" classes have been "re-loaded" by Catalyst under C<MyApp::Model>.
340 =head2 Include Authentication and Session Plugins
342 Edit C<lib/MyApp.pm> and update it as follows (everything below C<StackTrace> is new):
354 Session::Store::FastMmap
355 Session::State::Cookie
358 The C<Authentication> plugin supports
359 Authentication while the C<Session> plugins are required to maintain
360 state across multiple HTTP requests.
362 Note that the only required Authentication class is the main
363 one. This is a change that occured in version 0.09999_01
364 of the C<Authentication> plugin. You B<do not need> to specify a
365 particular Authentication::Store or Authentication::Credential plugin.
366 Instead, indicate the Store and Credential you want to use in your application
367 configuration (see below).
369 Note that there are several
370 options for L<Session::Store|Catalyst::Plugin::Session::Store>
371 (L<Session::Store::FastMmap|Catalyst::Plugin::Session::Store::FastMmap>
372 is generally a good choice if you are on Unix; try
373 L<Session::Store::File|Catalyst::Plugin::Session::Store::File> if you
374 are on Win32) -- consult
375 L<Session::Store|Catalyst::Plugin::Session::Store> and its subclasses
376 for additional information and options (for example to use a
377 database-backed session store).
380 =head2 Configure Authentication
382 Although C<__PACKAGE__-E<gt>config(name =E<gt> 'value');> is still
383 supported, newer Catalyst applications tend to place all configuration
384 information in C<myapp.yml> and automatically load this information into
385 C<MyApp-E<gt>config> using the
386 L<ConfigLoader|Catalyst::Plugin::ConfigLoader> plugin. Here, we need
387 to load several parameters that tell
388 L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication>
389 where to locate information in your database. To do this, edit the
390 C<myapp.yml> YAML and update it to match:
400 password_field: password
401 password_type: self_check
404 # This is the model object created by Catalyst::Model::DBIC from your
405 # schema (you created 'MyAppDB::User' but as the Catalyst startup
406 # debug messages show, it was loaded as 'MyApp::Model::MyAppDB::User').
407 # NOTE: Omit 'MyApp::Model' to avoid a component lookup issue in Catalyst 5.66
408 user_class: MyApp::Users
409 # This is the name of the field in your 'users' table that contains the user's name
413 ignore_fields_in_find: [ 'remote_name' ]
415 Inline comments in the code above explain how each field is being used.
417 B<TIP>: Although YAML uses a very simple and easy-to-ready format, it
418 does require the use of a consistent level of indenting. Be sure you
419 line up everything on a given 'level' with the same number of indents.
420 Also, be sure not to use C<tab> characters (YAML does not support them
421 because they are handled inconsistently across editors).
424 =head2 Add Login and Logout Controllers
426 Use the Catalyst create script to create two stub controller files:
428 $ script/myapp_create.pl controller Login
429 $ script/myapp_create.pl controller Logout
431 B<NOTE>: You could easily use a single controller here. For example,
432 you could have a C<User> controller with both C<login> and C<logout>
433 actions. Remember, Catalyst is designed to be very flexible, and leaves
434 such matters up to you, the designer and programmer.
436 Then open C<lib/MyApp/Controller/Login.pm>, locate the C<sub index :
437 Private> method (this was automatically inserted by the helpers when we
438 created the Login controller above), and delete this line:
440 $c->response->body('Matched MyApp::Controller::Login in Login.');
442 Then update it to match:
450 sub index : Private {
453 # Get the username and password from form
454 my $username = $c->request->params->{username} || "";
455 my $password = $c->request->params->{password} || "";
457 # If the username and password values were found in form
458 if ($username && $password) {
459 # Attempt to log the user in
460 if ($c->authenticate({ username => $username,
461 password => $password} )) {
462 # If successful, then let them use the application
463 $c->response->redirect($c->uri_for('/books/list'));
466 # Set an error message
467 $c->stash->{error_msg} = "Bad username or password.";
471 # If either of above don't work out, send to the login page
472 $c->stash->{template} = 'login.tt2';
475 This controller fetches the C<username> and C<password> values from the
476 login form and attempts to authenticate the user. If successful, it
477 redirects the user to the book list page. If the login fails, the user
478 will stay at the login page but receive an error message. If the
479 C<username> and C<password> values are not present in the form, the
480 user will be taken to the empty login form.
482 Note that we could have used something like C<sub default :Private>;
483 however, the use of C<default> actions is discouraged because it does
484 not receive path args as with other actions. The recommended practice
485 is to only use C<default> in C<MyApp::Controller::Root>.
487 Another option would be to use something like
488 C<sub base :Path :Args(0) {...}> (where the C<...> refers to the login
489 code shown in C<sub index : Private> above). We are using C<sub base
490 :Path :Args(0) {...}> here to specifically match the URL C</login>.
491 C<Path> actions (aka, "literal actions") create URI matches relative to
492 the namespace of the controller where they are defined. Although
493 C<Path> supports arguments that allow relative and absolute paths to be
494 defined, here we use an empty C<Path> definition to match on just the
495 name of the controller itself. The method name, C<base>, is arbitrary.
496 We make the match even more specific with the C<:Args(0)> action
497 modifier -- this forces the match on I<only> C</login>, not
498 C</login/somethingelse>.
500 Next, update the corresponding method in C<lib/MyApp/Controller/Logout.pm>
509 sub index : Private {
512 # Clear the user's state
515 # Send the user to the starting point
516 $c->response->redirect($c->uri_for('/'));
519 As with the login controller, be sure to delete the
520 C<$c->response->body('Matched MyApp::Controller::Logout in Logout.');>
521 line of the C<sub index>.
524 =head2 Add a Login Form TT Template Page
526 Create a login form by opening C<root/src/login.tt2> and inserting:
528 [% META title = 'Login' %]
531 <form method="post" action=" [% Catalyst.uri_for('/login') %] ">
535 <td><input type="text" name="username" size="40" /></td>
539 <td><input type="password" name="password" size="40" /></td>
542 <td colspan="2"><input type="submit" name="submit" value="Submit" /></td>
548 =head2 Add Valid User Check
550 We need something that provides enforcement for the authentication
551 mechanism -- a I<global> mechanism that prevents users who have not
552 passed authentication from reaching any pages except the login page.
553 This is generally done via an C<auto> action/method (prior to Catalyst
554 v5.66, this sort of thing would go in C<MyApp.pm>, but starting in
555 v5.66, the preferred location is C<lib/MyApp/Controller/Root.pm>).
557 Edit the existing C<lib/MyApp/Controller/Root.pm> class file and insert
558 the following method:
562 Check if there is a user and, if not, forward to login page
566 # Note that 'auto' runs after 'begin' but before your actions and that
567 # 'auto' "chain" (all from application path to most specific class are run)
568 # See the 'Actions' section of 'Catalyst::Manual::Intro' for more info.
572 # Allow unauthenticated users to reach the login page. This
573 # allows anauthenticated users to reach any action in the Login
574 # controller. To lock it down to a single action, we could use:
575 # if ($c->action eq $c->controller('Login')->action_for('index'))
576 # to only allow unauthenticated access to the C<index> action we
578 if ($c->controller eq $c->controller('Login')) {
582 # If a user doesn't exist, force login
583 if (!$c->user_exists) {
584 # Dump a log message to the development server debug output
585 $c->log->debug('***Root::auto User not found, forwarding to /login');
586 # Redirect the user to the login page
587 $c->response->redirect($c->uri_for('/login'));
588 # Return 0 to cancel 'post-auto' processing and prevent use of application
592 # User found, so return 1 to continue with processing after this 'auto'
596 B<Note:> Catalyst provides a number of different types of actions, such
597 as C<Local>, C<Regex>, and C<Private>. You should refer to
598 L<Catalyst::Manual::Intro> for a more detailed explanation, but the
599 following bullet points provide a quick introduction:
605 The majority of application use C<Local> actions for items that respond
606 to user requests and C<Private> actions for those that do not directly
607 respond to user input.
611 There are five types of C<Private> actions: C<begin>, C<end>,
612 C<default>, C<index>, and C<auto>.
616 With C<begin>, C<end>, C<default>, C<index> private actions, only the
617 most specific action of each type will be called. For example, if you
618 define a C<begin> action in your controller it will I<override> a
619 C<begin> action in your application/root controller -- I<only> the
620 action in your controller will be called.
624 Unlike the other actions where only a single method is called for each
625 request, I<every> auto action along the chain of namespaces will be
626 called. Each C<auto> action will be called I<from the application/root
627 controller down through the most specific class>.
631 By placing the authentication enforcement code inside the C<auto> method
632 of C<lib/MyApp/Controller/Root.pm> (or C<lib/MyApp.pm>), it will be
633 called for I<every> request that is received by the entire application.
636 =head2 Displaying Content Only to Authenticated Users
638 Let's say you want to provide some information on the login page that
639 changes depending on whether the user has authenticated yet. To do
640 this, open C<root/src/login.tt2> in your editor and add the following
641 lines to the bottom of the file:
645 # This code illustrates how certain parts of the TT
646 # template will only be shown to users who have logged in
648 [% IF Catalyst.user_exists %]
649 Please Note: You are already logged in as '[% Catalyst.user.username %]'.
650 You can <a href="[% Catalyst.uri_for('/logout') %]">logout</a> here.
652 You need to log in to use this application.
655 Note that this whole block is a comment because the "#" appears
656 immediate after the "[%" (with no spaces in between). Although it
657 can be a handy way to temporarily "comment out" a whole block of
658 TT code, it's probably a little too subtle for use in "normal"
663 Although most of the code is comments, the middle few lines provide a
664 "you are already logged in" reminder if the user returns to the login
665 page after they have already authenticated. For users who have not yet
666 authenticated, a "You need to log in..." message is displayed (note the
667 use of an IF-THEN-ELSE construct in TT).
670 =head2 Try Out Authentication
672 Press C<Ctrl-C> to kill the previous server instance (if it's still
673 running) and restart it:
675 $ script/myapp_server.pl
677 B<IMPORTANT NOTE>: If you happen to be using Internet Explorer, you may
678 need to use the command C<script/myapp_server.pl -k> to enable the
679 keepalive feature in the development server. Otherwise, the HTTP
680 redirect on successful login may not work correctly with IE (it seems to
681 work without -k if you are running the web browser and development
682 server on the same machine). If you are using browser a browser other
683 than IE, it should work either way. If you want to make keepalive the
684 default, you can edit C<script/myapp_server.pl> and change the
685 initialization value for C<$keepalive> to C<1>. (You will need to do
686 this every time you create a new Catalyst application or rebuild the
687 C<myapp_server.pl> script.)
689 Now trying going to L<http://localhost:3000/books/list> and you should
690 be redirected to the login page, hitting Shift+Reload if necessary (the
691 "You are already logged in" message should I<not> appear -- if it does,
692 click the C<logout> button and try again). Note the C<***Root::auto User
693 not found...> debug message in the development server output. Enter
694 username C<test01> and password C<mypass>, and you should be taken to
697 Open C<root/src/books/list.tt2> and add the following lines to the
701 <a href="[% Catalyst.uri_for('/login') %]">Login</a>
702 <a href="[% Catalyst.uri_for('form_create') %]">Create</a>
705 Reload your browser and you should now see a "Login" and "Create" links
706 at the bottom of the page (as mentioned earlier, you can update template
707 files without reloading the development server). Click the first link
708 to return to the login page. This time you I<should> see the "You are
709 already logged in" message.
711 Finally, click the C<You can logout here> link on the C</login> page.
712 You should stay at the login page, but the message should change to "You
713 need to log in to use this application."
716 =head1 USING PASSWORD HASHES
718 In this section we increase the security of our system by converting
719 from cleartext passwords to SHA-1 password hashes.
721 B<Note:> This section is optional. You can skip it and the rest of the
722 tutorial will function normally.
724 Note that even with the techniques shown in this section, the browser
725 still transmits the passwords in cleartext to your application. We are
726 just avoiding the I<storage> of cleartext passwords in the database by
727 using a SHA-1 hash. If you are concerned about cleartext passwords
728 between the browser and your application, consider using SSL/TLS, made
729 easy with the Catalyst plugin Catalyst::Plugin:RequireSSL.
732 =head2 Get a SHA-1 Hash for the Password
734 Catalyst uses the C<Digest> module to support a variety of hashing
735 algorithms. Here we will use SHA-1 (SHA = Secure Hash Algorithm).
736 First, we should compute the SHA-1 hash for the "mypass" password we are
737 using. The following command-line Perl script provides a "quick and
738 dirty" way to do this:
740 $ perl -MDigest::SHA -e 'print Digest::SHA::sha1_hex("mypass"), "\n"'
741 e727d1464ae12436e899a726da5b2f11d8381b26
744 B<Note:> You should probably modify this code for production use to
745 not read the password from the command line. By having the script
746 prompt for the cleartext password, it avoids having the password linger
747 in forms such as your C<.bash_history> files (assuming you are using
748 BASH as your shell). An example of such a script can be found in
752 =head2 Switch to SHA-1 Password Hashes in the Database
754 Next, we need to change the C<password> column of our C<users> table to
755 store this hash value vs. the existing cleartext password. Open
756 C<myapp03.sql> in your editor and enter:
759 -- Convert passwords to SHA-1 hashes
761 UPDATE users SET password = 'e727d1464ae12436e899a726da5b2f11d8381b26' WHERE id = 1;
762 UPDATE users SET password = 'e727d1464ae12436e899a726da5b2f11d8381b26' WHERE id = 2;
763 UPDATE users SET password = 'e727d1464ae12436e899a726da5b2f11d8381b26' WHERE id = 3;
765 Then use the following command to update the SQLite database:
767 $ sqlite3 myapp.db < myapp03.sql
769 B<Note:> We are using SHA-1 hashes here, but many other hashing
770 algorithms are supported. See C<Digest> for more information.
773 =head2 Enable SHA-1 Hash Passwords in
774 C<Catalyst::Plugin::Authentication::Store::DBIC>
776 Edit C<myapp.yml> and update it to match (the C<password_type> and
777 C<password_hash_type> are new, everything else is the same):
783 # Note this first definition would be the same as setting
784 # __PACKAGE__->config->{authentication}->{dbic}->{user_class} = 'MyAppDB::User'
785 # in lib/MyApp.pm (IOW, each hash key becomes a "name:" in the YAML file).
787 # This is the model object created by Catalyst::Model::DBIC from your
788 # schema (you created 'MyAppDB::User' but as the Catalyst startup
789 # debug messages show, it was loaded as 'MyApp::Model::MyAppDB::User').
790 # NOTE: Omit 'MyApp::Model' here just as you would when using
791 # '$c->model("MyAppDB::User)'
792 user_class: MyAppDB::User
793 # This is the name of the field in your 'users' table that contains the user's name
795 # This is the name of the field in your 'users' table that contains the password
796 password_field: password
797 # Other options can go here for hashed passwords
798 # Enabled hashed passwords
799 password_type: hashed
800 # Use the SHA-1 hashing algorithm
801 password_hash_type: SHA-1
804 =head2 Try Out the Hashed Passwords
806 Press C<Ctrl-C> to kill the previous server instance (if it's still
807 running) and restart it:
809 $ script/myapp_server.pl
811 You should now be able to go to L<http://localhost:3000/books/list> and
812 login as before. When done, click the "Logout" link on the login page
813 (or point your browser at L<http://localhost:3000/logout>).
815 B<Note:> If you receive the debug screen in your browser with a
816 C<Can't call method "stash" on an undefined value...> error message,
817 make sure that you are using v0.07 of
818 L<Catalyst::Plugin::Authorization::ACL|Catalyst::Plugin::Authorization::ACL>.
819 The following command can be a useful way to quickly dump the version number
820 of this module on your system:
822 perl -MCatalyst::Plugin::Authorization::ACL -e 'print $Catalyst::Plugin::Authorization::ACL::VERSION, "\n";'
825 =head1 USING THE SESSION FOR FLASH
827 As discussed in Part 3 of the tutorial, C<flash> allows you to set
828 variables in a way that is very similar to C<stash>, but it will
829 remain set across multiple requests. Once the value is read, it
830 is cleared (unless reset). Although C<flash> has nothing to do with
831 authentication, it does leverage the same session plugins. Now that
832 those plugins are enabled, let's go back and improve the "delete
833 and redirect with query parameters" code seen at the end of the
834 L<Basic CRUD|Catalyst::Manual::Tutorial::BasicCRUD> part of the
837 First, open C<lib/MyApp/Controller/Books.pm> and modify C<sub delete>
838 to match the following:
847 # $id = primary key of book to delete
848 my ($self, $c, $id) = @_;
850 # Search for the book and then delete it
851 $c->model('MyAppDB::Book')->search({id => $id})->delete_all;
853 # Use 'flash' to save information across requests until it's read
854 $c->flash->{status_msg} = "Book deleted";
856 # Redirect the user back to the list page with status msg as an arg
857 $c->response->redirect($c->uri_for('/books/list'));
860 Next, open C<root/lib/site/layout> and update the TT code to pull from
861 flash vs. the C<status_msg> query parameter:
863 <div id="header">[% PROCESS site/header %]</div>
866 <span class="message">[% status_msg || Catalyst.flash.status_msg %]</span>
867 <span class="error">[% error_msg %]</span>
871 <div id="footer">[% PROCESS site/footer %]</div>
876 Restart the development server and point your browser to
877 L<http://localhost:3000/books/url_create/Test/1/4> to create an extra
878 book. Click the "Return to list" link and delete the "Test" book you
879 just added. The C<flash> mechanism should retain our "Book deleted"
880 status message across the redirect.
882 B<NOTE:> While C<flash> will save information across multiple requests,
883 I<it does get cleared the first time it is read>. In general, this is
884 exactly what you want -- the C<flash> message will get displayed on
885 the next screen where it's appropriate, but it won't "keep showing up"
886 after that first time (unless you reset it). Please refer to
887 L<Catalyst::Plugin::Session|Catalyst::Plugin::Session> for additional
893 Kennedy Clark, C<hkclark@gmail.com>
895 Please report any errors, issues or suggestions to the author. The
896 most recent version of the Catalyst Tutorial can be found at
897 L<http://dev.catalyst.perl.org/repos/Catalyst/trunk/Catalyst-Manual/lib/Catalyst/Manual/Tutorial/>.
899 Copyright 2006, Kennedy Clark, under Creative Commons License
900 (L<http://creativecommons.org/licenses/by-nc-sa/2.5/>).