http://www.perlmonks.org?node_id=11131488

rajaman has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks,

How can I do the following in the pseudocode below:

1. the URL '/user/1' should dynamically point and rewrite to '/user/John-H' in the browser address bar and in link mouseover on the page. I have mapped id to value in the 'users' cache.
2. the URL (with a trailing slash) '/user/1/' should dynamically point and rewrite to '/user/John-H'
3. restrict the value of id to be an even number and less than 10.

Thank you!

#!/usr/bin/env perl use Mojolicious::Lite my $users = Mojo::Cache->new(max_keys => 50); $users->set(1 => 'John H'); $users->set(2 => 'Sam A'); #my $user = $users->get('1'); get '/user/:id' => [id => qr/\d+/] => sub ($c) { $c->render(text => $users{$id}); }; app->start;

Replies are listed 'Best First'.
Re: Mojolicious url rewriting
by haukex (Archbishop) on Apr 20, 2021 at 04:09 UTC
    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';

    Tests:

    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/app.pl') ); $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.

Re: Mojolicious url rewriting
by rajaman (Sexton) on Apr 21, 2021 at 00:56 UTC
    Thank you @haukex. Your example works awesome and it helps me understand the overall idea how routing is working in Mojo.

    Thank you @Bod. Yes, my original thought arose from htaccess-based url rewriting as shown here (and in detail in your link): https://webrewrite.com/url-rewriting-htaccess . So, I was wondering how something similar could be done in a Mojo code; and which one should be preferred.
Re: Mojolicious url rewriting
by Bod (Parson) on Apr 20, 2021 at 21:30 UTC

    If I understand correctly what you want to do, then doing it in code is perhaps not the best approach. Instead you can use the .htaccess file to control the redirect and display the appropriate URL in the browser.

Re: Mojolicious url rewriting
by perlfan (Vicar) on Apr 20, 2021 at 01:57 UTC
    I don't use newfangled web frameworks much, but it seems you're defining that anon sub handler wrong, but that might also be a mojo idiom. Usually you'd call a param (or something like that) on the place holder, like :id, inside the sub.