1. The purpose of the Crails Router
The Crails router recognizes URLs and dispatches them to a controller's action, or to custom lambdas.
1.1 Connecting URLs to code
When your Crails application receives an incoming request for:
GET /patients/17
it asks the router to match it to a controller action. If the first matching route is:
match_action("GET", "/patients/:id", PatientController, show);
the request is dispatched to the PatientController
controller's show
method with
{ "id": "17" }
in params
.
1.2 Configuring the Crails Router
The routes for your application or engine live in the file app/routes.cpp
and typically look like this:
#include "config/router.hpp"
#include "controllers/brands.hpp"
#include "controllers/products.hpp"
ApplicationRouter::ApplicationRouter()
{
match_action("GET", "brands", Brands, index);
scope("brands/:brand_id", [&]()
{
match_action("GET", "", Brands, show);
resource_actions("products", Products);
});
}
1.3 Using the router without controllers
For some simple web application, you might want to strip the controller layer altogether and directly handle queries using functions or lambdas. Here's an example of route matching using lambdas:
void hello_world(Crails::Request& request, std::function<void()> callback)
{
request.response.set_headers("Content-Type", "text/plain");
request.response.set_response(
Crails::HttpStatus::ok,
"Hello world !"
);
callback();
}
ApplicationRouter::ApplicationRouter()
{
match("GET", "hello_world", hello_world);
}
Request
object as a shared pointer,
so that it doesn't get deleted. Use request.shared_from_this()
to that end.
2. Resource Routing
Resource routing allows you to quickly declare all of the common routes for a given resourceful controller.
A single call to resources can declare all of the necessary routes for your
index
, show
, new
, edit
,
create
, update
, and destroy
actions.
2.1 Resources on the web
Browsers request pages from Crails by making a request for a URL using a specific HTTP method,
such as GET
, POST
, PATCH
, PUT
, and DELETE
.
Each method is a request to perform an operation on the resource. A resource route maps a number of related
requests to actions in a single controller.
When your Rails application receives an incoming request for:
DELETE /photos/17
it asks the router to map it to a controller action. If the first matching route is:
resource_actions("photos", PhotosController);
Crails would dispatch that request to the destroy
method on PhotosController with
{ id: "17" }
in params
.
2.2 CRUD, Verbs and Actions
In Rails, a resourceful route provides a mapping between HTTP verbs and URLs to controller actions. By convention, each action also maps to a specific CRUD operation in a database. A single entry in the routing file, such as:
resource_actions("photos", PhotosController);
creates seven different routes in your application, all mapping to the PhotosController
:
HTTP Verb | Path | Controller::Action | Used for |
---|---|---|---|
GET | /photos | PhotosController::index | display a list of all photos |
GET | /photos/new | PhotosController::new_ | return an HTML form for creating a new photo |
POST | /photos | PhotosController::create | create a new photo |
GET | /photos/:id | PhotosController::show | display a specific photo |
GET | /photos/:id/edit | PhotosController::edit | return an HTML form for editing a photo |
PUT | /photos/:id | PhotosController::update | update a specific photo |
DELETE | /photos/:id | PhotosController::destroy | delete a specific photo |
resource_actions("photos", PhotosController)
above a
match_action("GET", "photos/poll", PhotosController, poll)
, the show
action's route
for the resource_action line will be matched before the match_action
line.
To fix this, move the match_action
line above the resource_actions
line so that it
is matched first.
2.3 CRUD-only
resource_actions
is better fitted for HTML applications. When developing a web API, you
will probably be only interested in CRUD actions. In which case, you should use crud_actions
:
crud_actions("photos", PhotosController);
This helper works the same as resource_actions
, but strips away the edit
and
new_
routes, which you won't need in an API.
2.4 Scopes and Routing
You may wish to organize groups of controllers under a scope. Most commonly, you might group a number of
administrative controllers under an admin
scope.
scope("admin", [this]()
{
resource_actions("articles", Admin::ArticlesController);
resource_actions("comments", Admin::CommentsController);
});
This will create a number of routes for each of the articles and comments controller.
For Admin::ArticlesController
, Rails will create:
HTTP Verb | Path | Controller::Action |
---|---|---|
GET | /admin/articles | Admin::ArticlesController::index |
GET | /admin/articles/new | Admin::ArticlesController::new_ |
POST | /admin/articles | Admin::ArticlesController::create |
GET | /admin/articles/:id | Admin::ArticlesController::show |
GET | /admin/articles/:id/edit | Admin::ArticlesController::edit |
PUT | /admin/articles/:id | Admin::ArticlesController::update |
DELETE | /admin/articles/:id | Admin::ArticlesController::destroy |
3. Non-Resourceful Routes
3.1 Bound parameters
When you set up a regular route, you supply a series of symbols that Crails maps to parts of an incoming HTTP request. For example, consider this route:
match_action("GET", "/photos(/:id)?", PhotosController, display);
If an incoming request of /photos/1
is processed by this route (because it hasn't matched any
previous route in the file), then the result will be to invoke the display
method of the
PhotosController
, and to make the final parameter "1"
available as
params["id"]
.
This route will also route the incoming request of /photos
to PhotosController::display
,
since :id
is an optional parameter, denoted by question mark after the parentheses section.
3.2 Dynamic segments
You can set up as many dynamic segments within a regular route as you like. Any segment will be available
to the action as part of params
. If you set up this route:
match_action("GET", "/photos/:id/:user_id", PhotosController, show);
An incoming path of /photos/1/2
will be dispatched to the show action of the
PhotosController
. params["id"]
will be "1"
, and
params["user_id"]
will be "2"
.
3.3 Static segments
You can specify static segments when creating a route by not prepending a colon to a segment:
match_action("GET", "/photos/:id/with_user/:user_id", PhotosController, show);
This route would respond to paths such as /photos/1/with_user/2
.
In this case, params would be { id: "1", user_id: "2" }
.
3.4 The Query String
The params
will also include any parameters from the query string. For example, with this route:
match_action("GET", "/photos/:id", PhotosController, show);
An incoming path of /photos/1?user_id=2
will be dispatched to the show method of PhotosController.
params
will be { id: "1", user_id: "2" }
.
3.5 HTTP Verb Constraints
In general, you should use the GET, POST, PUT, PATCH, and DELETE methods to constrain a route to a particular verb. You can also left the verb parameter unspecified to match every verbs at once:
match_action("", "/photos", PhotosController, show);