Tag Archives: perl

  • -

Mojolicious routes and regular expressions

Tags : 

Recently I started using Mojolicious as a framework to implement a solution for a customer and I’d like to share with you a strange thing that made me waste a lot of time.

When defining a route for an API I noticed a difference between the Mojolicious::Lite implementation and the full Mojolicious one:

# Get metric data for service since X minutes ago
get '/metricdata/:service/:minutes/:metric' => sub {
	my $self = shift;
	my $acronym=uc($self->param('service'));
	my $minutes=$self->param('minutes');
	my $metric=$self->param('metric');
	my $extn = $self->stash('format');
[...]
	$self->respond_to(
	  json =>	{ json => $data },
	  csv  =>	{ text => $csv_data }
	);

The same in the full app:

 $r->route('/metricdata/:service/:minutes/:metric',
	  service => qr/[A-Z]+\d/,
	  minutes => qr/\d+/,
	  metric  => qr/.+/)->to('API#metricdata');
# Get metric data for service since X minutes ago
sub metricdata {
	my $self=shift;
	my $service=uc($self->param('service'));
	my $minutes=$self->param('minutes');
	my $metric=$self->param('metric');
	my $extn=$self->stash('format');
[...]
	$self->respond_to(
	  json =>	{ json => $data },
	  csv  =>	{ text => $csv_data }
	);

When calling this API like this:

curl http://localhost:3000/metricdata/SRV1/5/queuedepth.csv

In the first case $extnย ย gets the format (i.e. csv) of the requested data. In the second case Mojolicious “fails” to extract the extension and the value of the placeholder is the full last part queuedepth.csvย . Why?

The main difference between the two sub definition is expliciting the regular expression1 for the last placeholder. The entire :metric placeholder value (that matches .+) is not splitted and the format doesn’t pass through and your respond_to method will fail miserably.

The solution is to define the route without the regular expression (or with an horrible workaround: force format => ‘csv’ย  in the route definition and then extract the extension from $metricย  in the sub):

 $r->route('/metricdata/:service/:minutes/:metric',
	  service => qr/[A-Z]+\d/,
	  minutes => qr/\d+/)->to('API#metricdata');

I have yet to find an explanation in the documentation because I’d really like to restrict with a regular expression the route definition…

  1. by doing so I hoped to give more information and keep the code more readable []