Routing basics

Now let's take a closer look at routing . Routing is a description of the correspondence between URLs and processing.

I've used simple routing before.

get'/' => sub {...};
get'/ info' => sub {...};
get'/ date /: date' => sub {...};

Here's a little more detail about routing.

Placeholder

First, I will explain about placeholders.

Regular placeholder

I explained that if you add a colon at the beginning, that part can be obtained as a parameter.

get'/ date /: date' => sub {
  my $self = shift;
  my $date = $self->param('date');
};;

In Mojolicious, this is called a placeholder . Please note that this placeholder matches values ​​other than " / " and ". ".

For example, let's say you access with the URL "/ date / 20131015 / hello". This will result in unsuccessful routing. Instead of successfully routing and receiving "20131015 / hello" on "date", the routing itself does not succeed and the page cannot be found.

For example, let's say you access with the URL "/date/20131015.json". This will result in unsuccessful routing. Instead of being able to match the routing and receive "20131015.json" on the "date", the routing itself is unsuccessful and the page cannot be found.

Placeholders can also use "()" to distinguish that part is a placeholder.

get'/date/(:date).json'=> sub {...};

If you access with "/date/20130213.json", the routing is successful and you can receive "20130213" in the parameter "date".

Relax Placeholder

Normal placeholders match other than "/" and ".", But relax placeholders can be used to match other than ".". Relax placeholders start with a "#".

get'/ date / # date' => sub {...};

If you access with "/date/20130213.json", the routing will be successful and you will receive "20130213.json" in the parameter "date". You can get the part including ".".

The feature of Relax Placeholder is that you can also receive the part including "." As a parameter.

Wildcard Placeholder

Use wildcard placeholders to match all characters, including "/" and ".". Wildcard placeholders start with a "*".

get'/ date / * date' => sub {...};

If you access with "/ date / 20130123 / hello", the routing will be successful and you will receive "20130213 / hello" for the parameter "date". You can get the part including "/".

The feature of the wildcard placeholder is that the part including "/" can be received as a parameter like this.

HTTP method

Next, I would like to explain the HTTP method. The HTTP method is what you specify when sending an HTTP request. In general usage, the GET method is used to get the contents of the page, the POST method is used to send data by form, and the HEAD method is used to check the existence of the page.

Method name Usage
GET Get the contents of the page
POST Send data by form etc.
HEAD Confirm the existence of the page

There are other methods such as PUT method, DELETE method and PATCH method, but please remember these three here. Especially if you remember GET and POST, you will not have any trouble creating a website.

In Mojolicious::Lite, the following functions can be used corresponding to the above HTTP method.

get'/' => sub {...};
post'/' => sub {...};
head'/' => sub {...};
put'/' => sub {...};
del'/' => sub {...};
patch'/' => sub {...};

Match all HTTP methods

I think there are many cases where you want to receive both get and post at the same URL. In that case, use the any function. Matches all HTTP methods.

any'/' => sub {...};

Get HTTP method name

If you receive it with the any function, you may want to know which HTTP method the user used to access it. In such a case, you can get the Mojo::Message::Request object that expresses the HTTP request with the req method, and get the HTTP method with the method method.

my $http_method = $c->req->method;

Handle if the page is not found

In the case of a date, for example, I would like to write a process that the page cannot be found unless it is 8 digits. This is because it has no meaning if the number is other than 8 digits. In such cases, it's a good idea to let them know that it doesn't exist. You can use reply->not_found to draw the page "404 Not Found" yourself.

get'/ date /: date' => sub {
  my $self = shift;
  my $date = $self->param('date');
  
  # Draw "404 Not Found" if not in date format
  unless ($date = ~ / ^ [0-9] {8} $/) {
    $self->reply->not_found;
    return;
  }

  $self->render('date', date => $date);
};;

You can also use reply->exception instead to get a "500 error message" indicating an error.

$c->reply->exception('Error');

Preprocessing common to all routings

You may want to write a pre-process that is common to all routes. For example, you may want to redirect to a page called "/ login" if you are not logged in.

In such cases, you can use the under function to write preprocessing that is common to all routings. The sample below shows that you can get the stash value in a subroutine passed to the under function and use it in two different routes, "/ some1" and "some2".

use Mojolicious::Lite;

# Preprocessing
under sub {
  my $self = shift;
  
  $self->stash('name' =>'Kimoto');

  return 1;
};;

get'/ some1' => sub {
  my $self = shift;

  my $name = $self->stash('name');

  $self->render(text => $name);
};;

get'/ some2' => sub {
  my $self = shift;

  my $name = $self->stash('name');

  $self->render(text => $name);
};;

app->start;

In the above sample, 1 is returned as "return 1" in the under function, but this is necessary if you want to process the continuation that follows. If you do not want to continue processing after under, write "return" and return the undefined value.

If you change under as shown below, you can see that "Access deny" is displayed on the screen without continuing processing.

under sub {
  my $self = shift;
  
  my $continue = 0;
  
  # When not performing continuous processing
  unless ($continue) {
    $self->render(text =>'Access deny');
    return;
  }

  return 1;
};;

Routing sample

Let's write some sample code to understand routing.

use Mojolicious::Lite;

# Preprocessing
under sub {
  my $self = shift;
  
  # Do not allow if user is admin
  my $user = $self->url_for->path->parts->[0] //'';
  if ($user eq'admin') {
    
    $self->res->code(403);
    $self->render(text =>'Forbidden');
    return
  }
  
  return 1;
};;

# / Username / Project name / Directory name
# Or / user name / project name
get'/: user /: project / * dir' => {dir => undef} => sub {
  my $self = shift;#Parameter
  my $user = $self->param('user');
  my $project = $self->param('project');
  my $dir = $self->param('dir') //'Nothing';
  
  #Drawing
  $self->render(
    'index',
    user => $user,
    project => $project,
    dir => $dir
  );
};;

any'/ http-method' => sub {
  my $self = shift;

  #Get HTTP method
  my $http_method = $self->req->method;

  $self->render(text => "HTTP Method: $http_method");
};;

app->start;

__DATA__

@@index.html.ep
<%
  my $user = stash ('user');
  my $project = stash ('project');
  my $dir = stash ('dir');
%>
<html>
  <head>
    <title> Routing </title>
  </head>
  <body>
    User: <%= $user%> <br>
    Project: <%= $project%> <br>
    Directory: <%= $dir%> </br>
  </body>
</html>

Application description

This application can be accessed at the following URL:

/ Username / project name / directory name
/ Username / Project name

If you access it with "/kimoto/dog/dir1/readme.txt", it will be displayed as follows.

User: kimoto
Project: dog
Directory: dir1 / readme.txt

If you access it with "/ kimoto / dog", it will be displayed as follows.

User: kimoto
Project: dog
Directory: Nothing

This is because the routing is defined as follows:

get'/: user /: project / * dir' => {dir => undef} => sub {...};

If you access it with "/admin/dog/dir1/readme.txt", "Forbidden" will be displayed. This is because the following preprocessing is described.

# Preprocessing
under sub {
  my $self = shift;
  
  # Do not allow if user is admin
  my $user = $self->url_for->path->parts->[0] //'';
  if ($user eq'admin') {
    
    $self->res->code(403);
    $self->render(text =>'Forbidden');
    return
  }
  
  return 1;
};;

If the first part of the URL path is "admin", I'm trying to set the status code to "403" and return the text "Forbidden".

I will explain a little.

#Get the first part of the URL path
my $user = $self->url_for->path->parts->[0] //'';

First, use the url_for method to get the accessed URL. You will get a Mojo::URL object. To get the path part of this URL below, use the path method of the Mojo::URL class. You will get a Mojo::Path object. Then each part of the path is saved as an array, which can be obtained by the parts method of the Mojo::Path class.

"//" is a perl "defined-or operator" that allows you to assign the right side if the left side is an undefined value.

This way we are getting the first part of the URL path.

Next is the part to check with the user.

#If the user is admin, set the status code to 403 to display Forbidden
if ($user eq'admin') {
  
  $self->res->code(403);
  $self->render(text =>'Forbidden');
  return
}

You can get the response object by using the res method of the controller class. This is a Mojo::Message::Response object. You can set the status code using the code method of the Mojo::Message::Response class.

The next part gets the HTTP method name.

#Get HTTP method
my $http_method = $self->req->method;

When you access "/ http-method", "HTTP Method: GET" is displayed.

Associated Information