5

I'm in process of writing some web api, which returns data in JSON format.

For all incoming urls i need to return some result from cache if cache period doesnt expire.

This logic is universal for all URLs.

Question:

How to implement some common logic for all incoming URLs in Mojolicious::Lite?

I tried to do

any '/:bar' => [bar => qr/.+/] => sub {
 # ...
 # Return JSON object immediately from cache if it is applicable
 # ...
}

But this leads to very long page loading and

Template "bar.html.ep" not found

in morbo log (but get "/target" handler will be executed finally, after very long delay).

I am a perfect newbie in Mojo, so any help will be appreciated

Thanks!

drvtiny
  • 705
  • 3
  • 13
  • 3
    Can you show us the code in your handler? Mojolicious should not seek out an `html.ep` template to return JSON. Does your handler end with `$self->render( json => $object );` ? – mob Oct 07 '15 at 13:36
  • I think, http://mojolicio.us/perldoc/Mojolicious/Guides/Routing#Hooks may help me. But does it supported by the Lite version of Mojo?.. – drvtiny Oct 07 '15 at 15:46

2 Answers2

2

Yes, a before_dispatch hook seems like the right approach, and it does work with Mojolicious::Lite. Here's a proof-of-concept that will produce a new result for each unique request, but return repeat results for repeated requests. In this program, the regular request handler populates the cache, but if you wanted to separate that part from the main functions of your code you could do the caching in an after_dispatch hook.

use Mojolicious::Lite;

our %CACHE;

any '/:any' => sub {
    my $self = shift;
    my $param = $self->param('any');
    my $result = { reqtime => time, param => $param, number => rand };
    my $path = $self->req->url->path->to_string;
    $CACHE{$path} //= $result;
    $self->render( json => $result );
};

app->hook( before_dispatch => sub {
    my $c = shift;
    my $path = $c->req->url->path->to_string;
    if (defined($CACHE{$path})) {
        $c->render( json => $CACHE{$path}, status => 200 );
    }
} );
app->secrets([42])->start;

Sample run:

$ morbo cachedemo.pl >/dev/null 2>&1 &

$ for req in foo foo1 foo2 foo3 foo foo1
> do curl http://localhost:3000/$req ; echo ; sleep 1 ; done

{"number":0.848003210075227,"reqtime":1444254617,"param":"foo"}
{"number":0.0745738560703799,"reqtime":1444254618,"param":"foo1"}
{"number":0.484934245556467,"reqtime":1444254619,"param":"foo2"}
{"number":0.181112856445004,"reqtime":1444254620,"param":"foo3"}
{"number":0.848003210075227,"reqtime":1444254617,"param":"foo"}     <-- dup
{"number":0.0745738560703799,"reqtime":1444254618,"param":"foo1"}   <-- dup
mob
  • 117,087
  • 18
  • 149
  • 283
  • It's amazing, thank you very much! I did not know about "//=" construction in Perl and about possibility to start appication simply as app->secrets(["somesecret"])->start ... Could you tell me please, how to avoid execution of after_dispatch hook after making rendering in before_dispatch hook? Thank you in advance! – drvtiny Oct 08 '15 at 11:46
1

you can use * placeholder. Look here

Also put you application into developer mode:

Mojolicious->new( mode => 'developer' );
$app->mode( 'developer');

You will get pretty 404, 500 pages that will help you very much

Eugen Konkov
  • 22,193
  • 17
  • 108
  • 158