in reply to Mojolicious url rewriting

the URL '/user/1' should dynamically point and rewrite to '/user/John-H' in the browser address bar

By this I'm assuming you mean redirect?

and in link mouseover on the page

This is a little bit unclear: if you're generating the pages yourself, then it would be easiest to just insert the correct link in the first place, like I did in the code below. Otherwise, if your HTML really contains a link like <a href="/user/1"> and you want it to appear differently on mouseover, you'll have to solve that with JavaScript.

I have mapped id to value in the 'users' cache.

This seems a bit strange: a Mojo::Cache, being a cache, is transient, so I assume you're not actually using that as your data store in your actual application (and if you are, you shouldn't). You may have oversimplified your question a bit, since it's unclear what data source you're getting user IDs and names from, like a database? Anyway, I've just used a dummy.

restrict the value of id to be an even number and less than 10.

That's done simply enough with a regex of [02468], but since I suspect in your actual application you probably don't want to limit it to numbers below 10, in the following code I just limited it to any even integer.

Also note that your description is not self-consistent. You want /user/1 to rewrite to /user/John-H, but you also want to restrict IDs to even numbers. It's also unclear if/how you want to map 'John H' to 'John-H' and back, and how you want to handle '/user/NonexistentUser'.

Anyway, here's my best guess:

#!/usr/bin/perl use Mojolicious::Lite -signatures; helper users => sub ($c) { # probably a database in reality? state $users => { 2 => 'John H', 4 => 'Sam A', } }; get '/' => sub ($c) { $c->render(template => 'index') } => 'index'; get '/user/:id' => [id => qr/\d*[02468]/] => sub ($c) { my $id = $c->param('id'); if ( exists app->users->{$id} ) { $c->redirect_to('user_by_name', name=>app->users->{$id} ) } else { $c->reply->not_found } } => 'user_by_id'; get '/user/:name' => sub ($c) { # a very inefficient grep here, don't use this in production!! if ( grep { $_ eq $c->param('name') } values app->users->%* ) { $c->render( template=>'user' ) } else { $c->reply->not_found } } => 'user_by_name'; app->start; __DATA__ @@ layouts/main.html.ep <!DOCTYPE html> <html> <head><title><%= title %></title></head> <body> %= content </body> </html> @@ index.html.ep % layout 'main', title => 'Hello, World!'; <p>User List</p> <ul> % for my $id (sort {$a<=>$b} keys users->%*) { <li> %= link_to "User ID $id" => user_by_id => { id => $id } %= link_to users->{$id} => user_by_name=>{name=>users->{$id}} </li> % } </ul> @@ user.html.ep % layout 'main', title => 'User page'; <p>This is <%= $name %>'s Page</p> %= link_to Home => 'index';


use Mojo::Base -strict, -signatures; use Test::Mojo; use Test::More; use Mojo::File; use Mojo::Util qw/url_escape/; my $t = Test::Mojo->new( Mojo::File->new('/path/to/') ); $t->get_ok('/')->status_is(200)->content_like(qr/\bUser List\b/); $t->get_ok('/user/1')->status_is(404); $t->get_ok('/user/2') ->header_is(location => '/user/' . url_escape 'John H'); $t->get_ok('/user/3')->status_is(404); $t->get_ok('/user/4') ->header_is(location => '/user/' . url_escape 'Sam A'); $t->get_ok('/user/5')->status_is(404); $t->get_ok('/user/6')->status_is(404); $t->get_ok('/user/2/') # trailing slash ->header_is(location => '/user/' . url_escape 'John H'); $t->get_ok('/user/John H')->status_is(200) ->content_like(qr/\bThis is John H's Page\b/); $t->get_ok('/user/Sam A')->status_is(200) ->content_like(qr/\bThis is Sam A's Page\b/); $t->get_ok('/user/John Doe')->status_is(404); done_testing;

Minor typo fixes in text.