Ruby on Rails
Le routage
Recherche…
Introduction
Remarques
"Routage" est en général la manière dont les URL sont "gérées" par votre application. Dans le cas de Rails, c'est généralement quel contrôleur et quelle action de ce contrôleur traitera une URL entrante particulière. Dans les applications Rails, les routes sont généralement placées dans le fichier config/routes.rb
.
Routage des ressources (de base)
Les routes sont définies dans config/routes.rb
. Ils sont souvent définis comme un groupe d'itinéraires connexes, en utilisant les resources
ou resource
méthodes de resource
.
resources :users
créent les sept itinéraires suivants, tous UsersController
aux actions de UsersController
:
get '/users', to: 'users#index'
post '/users', to: 'users#create'
get '/users/new', to: 'users#new'
get '/users/:id/edit', to: 'users#edit'
get '/users/:id', to: 'users#show'
patch/put '/users/:id', to: 'users#update'
delete '/users/:id', to: 'users#destroy'
Les noms des actions sont affichés après le #
dans le paramètre to
ci-dessus. Les méthodes avec ces mêmes noms doivent être définies dans app/controllers/users_controller.rb
comme suit:
class UsersController < ApplicationController
def index
end
def create
end
# continue with all the other methods…
end
Vous pouvez limiter les actions générées avec only
ou except
:
resources :users, only: [:show]
resources :users, except: [:show, :index]
Vous pouvez afficher tous les itinéraires de votre application à tout moment en exécutant:
$ rake routes
$ rake routes
# OR
$ rails routes
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
Pour afficher uniquement les itinéraires mappés sur un contrôleur particulier:
$ rake routes -c static_pages
static_pages_home GET /static_pages/home(.:format) static_pages#home
static_pages_help GET /static_pages/help(.:format) static_pages#help
$ rake routes -c static_pages
static_pages_home GET /static_pages/home(.:format) static_pages#home
static_pages_help GET /static_pages/help(.:format) static_pages#help
# OR
$ rails routes -c static_pages
static_pages_home GET /static_pages/home(.:format) static_pages#home
static_pages_help GET /static_pages/help(.:format) static_pages#help
Vous pouvez rechercher dans les itinéraires en utilisant l'option -g
. Cela montre toute route qui correspond partiellement au nom de la méthode d'assistance, au chemin de l'URL ou au verbe HTTP:
$ rake routes -g new_user # Matches helper method
$ rake routes -g POST # Matches HTTP Verb POST
$ rake routes -g new_user # Matches helper method
$ rake routes -g POST # Matches HTTP Verb POST
# OR
$ rails routes -g new_user # Matches helper method
$ rails routes -g POST # Matches HTTP Verb POST
De plus, lorsque vous exécutez le serveur rails
en mode développement, vous pouvez accéder à une page Web qui affiche tous vos itinéraires avec un filtre de recherche, priorisé de haut en bas, à <hostname>/rails/info/routes
. Il ressemblera à ceci:
Assistant | Verbe HTTP | Chemin | Action du contrôleur |
---|---|---|---|
Chemin / URL | [Path Match] | ||
chemin_utilisateur | OBTENIR | /users(.:format) | utilisateurs # index |
POSTER | /users(.:format) | les utilisateurs # créent | |
new_user_path | OBTENIR | /users/new(.:format) | utilisateurs # new |
edit_user_path | OBTENIR | /users/:id/edit(.:format) | utilisateurs # modifier |
chemin_utilisateur | OBTENIR | /users/:id(.:format) | utilisateurs # montrent |
PIÈCE | /users/:id(.:format) | utilisateurs # mise à jour | |
METTRE | /users/:id(.:format) | utilisateurs # mise à jour | |
EFFACER | /users/:id(.:format) | les utilisateurs # détruisent |
Les routes peuvent être déclarées disponibles uniquement pour les membres (pas les collections) à l'aide de la resource
méthode au lieu de resources
dans routes.rb
. Avec la resource
, une route d' index
n'est pas créée par défaut, mais uniquement lorsque vous en demandez explicitement une comme ceci:
resource :orders, only: [:index, :create, :show]
Contraintes
Vous pouvez filtrer les routes disponibles à l'aide de contraintes.
Il existe plusieurs manières d’utiliser des contraintes, notamment:
Par exemple, une contrainte basée sur la demande pour autoriser uniquement une adresse IP spécifique à accéder à une route:
constraints(ip: /127\.0\.0\.1$/) do
get 'route', to: "controller#action"
end
Voir d'autres exemples similaires ActionDispatch :: Routing :: Mapper :: Scoping .
Si vous voulez faire quelque chose de plus complexe, vous pouvez utiliser des contraintes plus avancées et créer une classe pour envelopper la logique:
# lib/api_version_constraint.rb
class ApiVersionConstraint
def initialize(version:, default:)
@version = version
@default = default
end
def version_header
"application/vnd.my-app.v#{@version}"
end
def matches?(request)
@default || request.headers["Accept"].include?(version_header)
end
end
# config/routes.rb
require "api_version_constraint"
Rails.application.routes.draw do
namespace :v1, constraints: ApiVersionConstraint.new(version: 1, default: true) do
resources :users # Will route to app/controllers/v1/users_controller.rb
end
namespace :v2, constraints: ApiVersionConstraint.new(version: 2) do
resources :users # Will route to app/controllers/v2/users_controller.rb
end
end
Un formulaire, plusieurs boutons de soumission
Vous pouvez également utiliser la valeur des balises submit d'un formulaire comme contrainte pour acheminer une action différente. Si vous avez un formulaire avec plusieurs boutons d'envoi (par exemple "preview" et "submit"), vous pouvez capturer cette contrainte directement dans vos routes.rb
, au lieu d'écrire du javascript pour changer l'URL de destination du formulaire. Par exemple, avec le gem commit_param_routing, vous pouvez tirer parti de rails submit_tag
Rails submit_tag
premier paramètre submit_tag
vous permet de modifier la valeur de votre paramètre de validation de formulaire
# app/views/orders/mass_order.html.erb
<%= form_for(@orders, url: mass_create_order_path do |f| %>
<!-- Big form here -->
<%= submit_tag "Preview" %>
<%= submit_tag "Submit" %>
# => <input name="commit" type="submit" value="Preview" />
# => <input name="commit" type="submit" value="Submit" />
...
<% end %>
# config/routes.rb
resources :orders do
# Both routes below describe the same POST URL, but route to different actions
post 'mass_order', on: :collection, as: 'mass_order',
constraints: CommitParamRouting.new('Submit'), action: 'mass_create' # when the user presses "submit"
post 'mass_order', on: :collection,
constraints: CommitParamRouting.new('Preview'), action: 'mass_create_preview' # when the user presses "preview"
# Note the `as:` is defined only once, since the path helper is mass_create_order_path for the form url
# CommitParamRouting is just a class like ApiVersionContraint
end
Itinéraires de portée
Rails propose plusieurs méthodes pour organiser vos itinéraires.
Portée par URL :
scope 'admin' do
get 'dashboard', to: 'administration#dashboard'
resources 'employees'
end
Cela génère les routes suivantes
get '/admin/dashboard', to: 'administration#dashboard'
post '/admin/employees', to: 'employees#create'
get '/admin/employees/new', to: 'employees#new'
get '/admin/employees/:id/edit', to: 'employees#edit'
get '/admin/employees/:id', to: 'employees#show'
patch/put '/admin/employees/:id', to: 'employees#update'
delete '/admin/employees/:id', to: 'employees#destroy'
Du côté du serveur, il peut être plus judicieux de conserver certaines vues dans un sous-dossier différent, afin de séparer les vues d’administration des vues des utilisateurs.
Portée par module
scope module: :admin do
get 'dashboard', to: 'administration#dashboard'
end
module
recherche les fichiers du contrôleur sous le sous-dossier du nom donné
get '/dashboard', to: 'admin/administration#dashboard'
Vous pouvez renommer le préfixe des aides de chemin en ajoutant un paramètre as
scope 'admin', as: :administration do
get 'dashboard'
end
# => administration_dashboard_path
Rails fournit un moyen pratique de faire tout ce qui précède, en utilisant la méthode d' namespace
. Les déclarations suivantes sont équivalentes
namespace :admin do
end
scope 'admin', module: :admin, as: :admin
Portée par contrôleur
scope controller: :management do
get 'dashboard'
get 'performance'
end
Cela génère ces routes
get '/dashboard', to: 'management#dashboard'
get '/performance', to: 'management#performance'
Nidification peu profonde
Les itinéraires de ressources acceptent une option :shallow
qui permet de raccourcir les URL lorsque cela est possible. Les ressources ne doivent pas être imbriquées à plus d'un niveau. Une façon d'éviter cela est de créer des routes peu profondes. L'objectif est de laisser de côté les segments d'URL de la collection parent où ils ne sont pas nécessaires. Le résultat final est que les seules routes imbriquées générées sont pour :index
:create
et :new
actions. Les autres sont conservés dans leur propre contexte d'URL peu profond. Il existe deux options pour la portée des itinéraires peu profonds personnalisés:
: shallow_path : préfixes des chemins de membre avec un paramètre spécifié
scope shallow_path: "sekret" do resources :articles do resources :comments, shallow: true end end
: shallow_prefix : ajoute les paramètres spécifiés aux helpers nommés
scope shallow_prefix: "sekret" do resources :articles do resources :comments, shallow: true end end
Nous pouvons également illustrer des routes shallow
en:
resources :auctions, shallow: true do
resources :bids do
resources :comments
end
end
alternativement codé comme suit (si vous êtes bloqué):
resources :auctions do
shallow do
resources :bids do
resources :comments
end
end
end
Les itinéraires résultants sont:
Préfixe | Verbe | Modèle d'URI |
---|---|---|
bid_comments | OBTENIR | /bids/:bid_id/comments(.:format) |
POSTER | /bids/:bid_id/comments(.:format) | |
new_bid_comment | OBTENIR | /bids/:bid_id/comments/new(.:format) |
edit_comment | OBTENIR | /comments/:id/edit(.:format) |
commentaire | OBTENIR | /comments/:id(.:format) |
PIÈCE | /comments/:id(.:format) | |
METTRE | /comments/:id(.:format) | |
EFFACER | /comments/:id(.:format) | |
auction_bids | OBTENIR | /auctions/:auction_id/bids(.:format) |
POSTER | /auctions/:auction_id/bids(.:format) | |
new_auction_bid | OBTENIR | /auctions/:auction_id/bids/new(.:format) |
edit_bid | OBTENIR | /bids/:id/edit(.:format) |
offre | OBTENIR | /bids/:id(.:format) |
PIÈCE | /bids/:id(.:format) | |
METTRE | /bids/:id(.:format) | |
EFFACER | /bids/:id(.:format) | |
les enchères | OBTENIR | /auctions(.:format) |
POSTER | /auctions(.:format) | |
new_auction | OBTENIR | /auctions/new(.:format) |
edit_auction | OBTENIR | /auctions/:id/edit(.:format) |
enchères | OBTENIR | /auctions/:id(.:format) |
PIÈCE | /auctions/:id(.:format) | |
METTRE | /auctions/:id(.:format) | |
EFFACER | /auctions/:id(.:format) |
Si vous analysez les routes générées avec soin, vous remarquerez que les parties imbriquées de l'URL ne sont incluses que lorsqu'elles sont nécessaires pour déterminer les données à afficher.
Les soucis
Pour éviter la répétition dans les routes imbriquées, les préoccupations offrent un excellent moyen de partager des ressources communes réutilisables. Pour créer un problème, utilisez la méthode concern
dans le fichier routes.rb
. La méthode attend un symbole et un bloc:
concern :commentable do
resources :comments
end
Tout en ne créant aucune route elle-même, ce code permet d'utiliser l'attribut :concerns
sur une ressource. L'exemple le plus simple serait:
resource :page, concerns: :commentable
La ressource imbriquée équivalente ressemblerait à ceci:
resource :page do
resource :comments
end
Cela permettrait, par exemple, de créer les itinéraires suivants:
/pages/#{page_id}/comments
/pages/#{page_id}/comments/#{comment_id}
Pour que les préoccupations soient significatives, il doit y avoir plusieurs ressources qui utilisent le problème. Des ressources supplémentaires peuvent utiliser l'une des syntaxes suivantes pour appeler le problème:
resource :post, concerns: %i(commentable)
resource :blog do
concerns :commentable
end
Redirection
Vous pouvez effectuer la redirection dans les itinéraires Rails comme suit:
get '/stories', to: redirect('/posts')
match "/abc" => redirect("http://example.com/abc")
Vous pouvez également rediriger tous les itinéraires inconnus vers un chemin donné:
match '*path' => redirect('/'), via: :get
# or
get '*path' => redirect('/')
match '*path' => redirect('/')
Itinéraires de membre et de collection
La définition d'un bloc membre dans une ressource crée un itinéraire pouvant agir sur un membre individuel de cet itinéraire basé sur une ressource:
resources :posts do
member do
get 'preview'
end
end
Cela génère l'itinéraire de membre suivant:
get '/posts/:id/preview', to: 'posts#preview'
# preview_post_path
Les itinéraires de collecte permettent de créer des itinéraires pouvant agir sur une collection d'objets ressource:
resources :posts do
collection do
get 'search'
end
end
Cela génère l'itinéraire de collecte suivant:
get '/posts/search', to: 'posts#search'
# search_posts_path
Une syntaxe alternative:
resources :posts do
get 'preview', on: :member
get 'search', on: :collection
end
Paramètres d'URL avec un point
Si vous souhaitez prendre en charge un paramètre d'URL plus complexe qu'un numéro d'identification, vous pouvez rencontrer des problèmes avec l'analyseur si la valeur contient un point. Tout ce qui suit une période sera considéré comme un format (c.-à-d. Json, xml).
Vous pouvez contourner cette limitation en utilisant une contrainte pour élargir l'entrée acceptée.
Par exemple, si vous souhaitez référencer un enregistrement d'utilisateur par adresse électronique dans l'URL:
resources :users, constraints: { id: /.*/ }
Route racine
Vous pouvez ajouter un itinéraire de page d'accueil à votre application avec la méthode root
.
# config/routes.rb
Rails.application.routes.draw do
root "application#index"
# equivalent to:
# get "/", "application#index"
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
def index
render "homepage"
end
end
Et dans les terminaux, les rake routes
( rake routes
rails routes
dans Rails 5) produiront:
root GET / application#index
Étant donné que la page d'accueil est généralement l'itinéraire le plus important et que les itinéraires sont classés par ordre de priorité dans l'ordre dans lequel ils apparaissent, l'itinéraire root
doit généralement être le premier de votre fichier de routes.
Actions RESTful supplémentaires
resources :photos do member do get 'preview' end collection do get 'dashboard' end end
Cela crée les routes suivantes en plus des 7 routes RESTful par défaut :
get '/photos/:id/preview', to: 'photos#preview'
get '/photos/dashboards', to: 'photos#dashboard'
Si vous voulez faire cela pour des lignes simples, vous pouvez utiliser:
resources :photos do get 'preview', on: :member get 'dashboard', on: :collection end
Vous pouvez également ajouter une action au chemin /new
:
resources :photos do get 'preview', on: :new end
Qui va créer:
get '/photos/new/preview', to: 'photos#preview'
Soyez attentif lorsque vous ajoutez des actions à vos routes RESTful, vous manquez probablement une autre ressource!
Portée disponible locale
Si votre application est disponible dans différentes langues, vous affichez généralement les paramètres régionaux actuels dans l'URL.
scope '/(:locale)', locale: /#{I18n.available_locales.join('|')}/ do
root 'example#root'
# other routes
end
Votre racine sera accessible via les paramètres régionaux définis dans I18n.available_locales
.
Monter une autre application
le montage est utilisé pour monter une autre application (essentiellement une application en rack) ou des moteurs de rails à utiliser dans l'application en cours
syntaxe:
mount SomeRackApp, at: "some_route"
Vous pouvez maintenant accéder aux applications montées ci-dessus en utilisant l'assistant de route some_rack_app_path
ou some_rack_app_url
.
Mais si vous voulez renommer ce nom d’assistant, vous pouvez le faire en tant que:
mount SomeRackApp, at: "some_route", as: :myapp
Cela générera les myapp_path
et myapp_url
qui peuvent être utilisés pour naviguer dans cette application montée.
Itinéraires de redirections et de caractères génériques
Si vous souhaitez fournir une URL hors de la convenance pour votre utilisateur, mais la mapper directement à une autre que vous utilisez déjà. Utilisez une redirection:
# config/routes.rb
TestApp::Application.routes.draw do
get 'courses/:course_name' => redirect('/courses/%{course_name}/lessons'), :as => "course"
end
Eh bien, ça a été intéressant rapidement. Le principe de base consiste à utiliser la méthode #redirect
pour envoyer une route à une autre. Si votre itinéraire est assez simple, c'est une méthode très simple. Mais si vous voulez aussi envoyer les paramètres d'origine, vous devez faire un peu de gymnastique en capturant le paramètre dans %{here}
. Notez les guillemets simples autour de tout.
Dans l'exemple ci-dessus, nous avons également renommé la route pour plus de commodité en utilisant un alias avec le paramètre: as. Cela nous permet d'utiliser ce nom dans des méthodes telles que les aides #_path. Encore une fois, testez vos $ rake routes
avec des questions.
Diviser les itinéraires en plusieurs fichiers
Si votre fichier de routes est extrêmement volumineux, vous pouvez placer vos itinéraires dans plusieurs fichiers et inclure chacun des fichiers avec la méthode require_relative
de Ruby:
config/routes.rb
: YourAppName::Application.routes.draw do
require_relative 'routes/admin_routes'
require_relative 'routes/sidekiq_routes'
require_relative 'routes/api_routes'
require_relative 'routes/your_app_routes'
end
config/routes/api_routes.rb
: YourAppName::Application.routes.draw do
namespace :api do
# ...
end
end
Routes imbriquées
Si vous souhaitez ajouter des routes imbriquées, vous pouvez écrire le code suivant dans le fichier routes.rb
.
resources :admins do
resources :employees
end
Cela générera les itinéraires suivants:
admin_employees GET /admins/:admin_id/employees(.:format) employees#index
POST /admins/:admin_id/employees(.:format) employees#create
new_admin_employee GET /admins/:admin_id/employees/new(.:format) employees#new
edit_admin_employee GET /admins/:admin_id/employees/:id/edit(.:format) employees#edit
admin_employee GET /admins/:admin_id/employees/:id(.:format) employees#show
PATCH /admins/:admin_id/employees/:id(.:format) employees#update
PUT /admins/:admin_id/employees/:id(.:format) employees#update
DELETE /admins/:admin_id/employees/:id(.:format) employees#destroy
admins GET /admins(.:format) admins#index
POST /admins(.:format) admins#create
new_admin GET /admins/new(.:format) admins#new
edit_admin GET /admins/:id/edit(.:format) admins#edit
admin GET /admins/:id(.:format) admins#show
PATCH /admins/:id(.:format) admins#update
PUT /admins/:id(.:format) admins#update
DELETE /admins/:id(.:format) admins#destroy