Ruby on Rails
ActionController
Recherche…
Introduction
Action Controller est le C dans MVC. Une fois que le routeur a déterminé quel contrôleur utiliser pour une demande, le contrôleur est responsable de donner un sens à la demande et de produire la sortie.
Le contrôleur reçoit la demande, récupère ou enregistre les données d'un modèle et utilise une vue pour créer une sortie. Un contrôleur peut être considéré comme un intermédiaire entre les modèles et les vues. Il met les données du modèle à la disposition de la vue pour les afficher à l'utilisateur et enregistre ou met à jour les données utilisateur sur le modèle.
JSON de sortie au lieu de HTML
class UsersController < ApplicationController def index hashmap_or_array = [{ name: "foo", email: "[email protected]" }] respond_to do |format| format.html { render html: "Hello World" } format.json { render json: hashmap_or_array } end end end
De plus, vous aurez besoin de l'itinéraire:
resources :users, only: [:index]
Cela répondra de deux manières différentes aux requêtes /users
:
- Si vous visitez
/users
ou/users.html
, il affichera une page HTML avec le contenuHello World
- Si vous visitez
/users.json
, un objet JSON contenant:
[ { "name": "foo", "email": "[email protected]" } ]
Vous pouvez omettre format.html { render inline: "Hello World" }
si vous voulez vous assurer que votre itinéraire ne répondra qu'aux requêtes JSON.
Contrôleurs (de base)
class UsersController < ApplicationController def index respond_to do |format| format.html { render html: "Hello World" } end end end
Ceci est un contrôleur de base, avec l'ajout de l'itinéraire suivant (dans routes.rb):
resources :users, only: [:index]
Affiche le message Hello World
dans une page Web lorsque vous accédez à l'URL /users
Paramètres
Les contrôleurs ont accès aux paramètres HTTP (vous les connaissez peut-être sous le ?name=foo
dans les URL, mais Ruby on Rails gère également différents formats!) Et affiche différentes réponses basées sur ces derniers. Il n'y a pas de moyen de distinguer les paramètres GET et POST, mais vous ne devriez pas le faire dans tous les cas.
class UsersController < ApplicationController
def index
respond_to do |format|
format.html do
if params[:name] == "john"
render html: "Hello John"
else
render html: "Hello someone"
end
end
end
end
end
Comme d'habitude notre itinéraire:
resources :users, only: [:index]
Accédez à l'URL /users?name=john
et le résultat sera Hello John
, access /users?name=whatever
et le résultat sera Hello someone
Paramètres de filtrage (Basic)
class UsersController < ApplicationController def index respond_to do |format| format.html do render html: "Hello #{ user_params[:name] } user_params[:sentence]" end end end private def user_params if params[:name] == "john" params.permit(:name, :sentence) else params.permit(:name) end end end
Vous pouvez autoriser (ou rejeter) certains paramètres de manière à ne laisser passer que ce que vous voulez et vous ne rencontrerez pas de mauvaises surprises, comme les options de paramétrage utilisateur qui ne sont pas censées être modifiées.
Visiting /users?name=john&sentence=developer
affichera Hello john developer
, en visitant toutefois /users?name=smith&sentence=spy
affichera Hello smith
only, car :sentence
n'est autorisée que lorsque vous accédez en tant que john
Redirection
En supposant que l'itinéraire:
resources :users, only: [:index]
Vous pouvez rediriger vers une URL différente en utilisant:
class UsersController def index redirect_to "http://stackoverflow.com/" end end
Vous pouvez revenir à la page précédente que l'utilisateur a visitée en utilisant:
redirect_to :back
Notez que dans Rails 5, la syntaxe de redirection est différente:
redirect_back fallback_location: "http://stackoverflow.com/"
Qui tentera de rediriger vers la page précédente et au cas où cela ne serait pas possible (le navigateur bloque l’en-tête HTTP_REFERRER), il sera redirigé vers :fallback_location
Utiliser des vues
En supposant que l'itinéraire:
resources :users, only: [:index]
Et le contrôleur:
class UsersController < ApplicationController def index respond_to do |format| format.html { render } end end end
La vue app/users/index.html.erb
sera rendue. Si la vue est:
Hello <strong>World</strong>
Le résultat sera une page Web avec le texte: "Hello World "
Si vous souhaitez afficher une vue différente, vous pouvez utiliser:
render "pages/home"
Et le fichier app/views/pages/home.html.erb
sera utilisé à la place.
Vous pouvez transmettre des variables à des vues à l'aide de variables d'instance de contrôleur:
class UsersController < ApplicationController def index @name = "john" respond_to do |format| format.html { render } end end end
Et dans le fichier app/views/users/index.html.erb
vous pouvez utiliser @name
:
Hello <strong><%= @name %></strong>
Et le résultat sera: "Bonjour John "
Une note importante autour de la syntaxe de rendu, vous pouvez omettre entièrement la syntaxe de render
, Rails suppose que si vous l'omettez. Alors:
class UsersController < ApplicationController def index respond_to do |format| format.html { render } end end end
Peut être écrit à la place comme:
class UsersController < ApplicationController def index respond_to do |format| format.html end end end
Rails est assez intelligent pour comprendre qu'il doit rendre le fichier app/views/users/index.html.erb
.
404 lorsque l'enregistrement n'a pas été trouvé
Erreur de récupération de l'enregistrement introuvable au lieu d'afficher une exception ou une page blanche:
class ApplicationController < ActionController::Base
# ... your other stuff here
rescue_from ActiveRecord::RecordNotFound do |exception|
redirect_to root_path, 404, alert: 'Record not found'
end
end
Contrôleur REST de base
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
def index
@posts = Post.all
end
def show
end
def new
@post = Post.new
end
def edit
end
def create
@post = Post.new(post_params)
respond_to do |format|
if @post.save
format.html { redirect_to @post, notice: 'Post was successfully created.' }
format.json { render :show, status: :created, location: @post }
else
format.html { render :new }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if @post.update(post_params)
format.html { redirect_to @post.company, notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: @post }
else
format.html { render :edit }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
def destroy
@post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_post
@post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :body, :author)
end
end
Afficher les pages d'erreur pour les exceptions
Si vous voulez afficher à vos utilisateurs des erreurs significatives au lieu de simples "désolé, quelque chose a mal tourné", Rails a un utilitaire intéressant à cet effet.
Ouvrez le fichier app/controllers/application_controller.rb
et vous devriez trouver quelque chose comme ceci:
class ApplicationController < ActionController::Base protect_from_forgery with: :exception end
Nous pouvons maintenant ajouter un rescue_from
pour récupérer des erreurs spécifiques:
class ApplicationController < ActionController::Base protect_from_forgery with: :exception rescue_from ActiveRecord::RecordNotFound, with: :record_not_found private def record_not_found render html: "Record <strong>not found</strong>", status: 404 end end
Il est recommandé de ne pas utiliser Exception
ou StandardError
sinon Rails ne pourra pas afficher de pages utiles en cas d'erreur.
Des filtres
Les filtres sont des méthodes exécutées "avant", "après" ou "autour" d'une action de contrôleur. Ils sont hérités, donc si vous en définissez dans votre ApplicationController
ils seront exécutés pour chaque requête reçue par votre application.
Avant filtre
Avant que les filtres ne soient exécutés avant l'action du contrôleur et peuvent arrêter la demande (et / ou la redirection). Une utilisation courante consiste à vérifier si un utilisateur est connecté:
class ApplicationController < ActionController::Base
before_action :authenticate_user!
def authenticate_user!
redirect_to some_path unless user_signed_in?
end
end
Avant que les filtres ne soient exécutés sur les requêtes avant que la requête n'arrive à l'action du contrôleur. Il peut renvoyer une réponse elle-même et contourner complètement l'action.
D'autres utilisations courantes des filtres avant sont la validation de l'authentification d'un utilisateur avant de lui accorder l'accès à l'action désignée pour traiter leur demande. Je les ai également vus utilisés pour charger une ressource de la base de données, vérifier les autorisations sur une ressource ou gérer les redirections dans d'autres circonstances.
Après filtre
Après les filtres sont similaires à ceux "avant", mais comme ils sont exécutés après l'exécution de l'action, ils ont accès à l'objet de réponse qui est sur le point d'être envoyé. Donc, en bref, après l'exécution des filtres une fois l'action terminée. Il peut modifier la réponse. La plupart du temps, si quelque chose est fait dans un filtre après, cela peut être fait dans l'action elle-même, mais s'il y a une logique à exécuter après avoir exécuté un ensemble d'actions, alors un filtre après est un bon endroit il.
Généralement, j'ai vu après et autour des filtres utilisés pour la journalisation.
Filtre autour
Autour des filtres peuvent avoir la logique avant et après l'action en cours d'exécution. Il cède simplement à l'action dans n'importe quel endroit est nécessaire. Notez qu'il n'a pas besoin de céder à l'action et peut s'exécuter sans le faire comme un filtre avant.
Les filtres Around sont chargés d’exécuter les actions associées, tout comme les middlewares Rack fonctionnent.
Les callbacks entourent l'exécution des actions. Vous pouvez écrire un rappel autour de deux styles différents. Dans le premier, le rappel est un morceau de code unique. Ce code est appelé avant l'exécution de l'action. Si le code de rappel appelle le rendement, l'action est exécutée. Une fois l'action terminée, le code de rappel continue à s'exécuter. Ainsi, le code avant le rendement est comme un rappel d'action avant et le code après le rendement est le rappel après action. Si le code de rappel n'appelle jamais le rendement. L'action n'est pas exécutée, c'est la même chose que d'avoir un retour de rappel d'action avant false.
Voici un exemple du filtre autour:
around_filter :catch_exceptions
private
def catch_exceptions
begin
yield
rescue Exception => e
logger.debug "Caught exception! #{e.message}"
end
end
Cela interceptera toute exception et mettra le message dans votre journal. Vous pouvez utiliser des filtres pour la gestion des exceptions, la configuration et le démontage, ainsi que de nombreux autres cas.
Seulement et sauf
Tous les filtres peuvent être appliqués à des actions spécifiques, en utilisant :only
and :except
:
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update]
# ... controller actions
# Define your filters as controller private methods
private
def set_product
@product = Product.find(params[:id])
end
end
Filtre à sauter
Tous les filtres (ceux hérités aussi) peuvent également être ignorés pour certaines actions spécifiques:
class ApplicationController < ActionController::Base
before_action :authenticate_user!
def authenticate_user!
redirect_to some_path unless user_signed_in?
end
end
class HomeController < ApplicationController
skip_before_action :authenticate_user!, only: [:index]
def index
end
end
Comme ils sont hérités, les filtres peuvent également être définis dans un contrôleur "parent" d' namespace
. Dites par exemple que vous avez un espace de noms admin
et que vous voulez bien sûr que seuls les utilisateurs administrateurs puissent y accéder. Vous pourriez faire quelque chose comme ça:
# config/routes.rb
namespace :admin do
resources :products
end
# app/controllers/admin_controller.rb
class AdminController < ApplicationController
before_action :authenticate_admin_user!
private
def authenticate_admin_user!
redirect_to root_path unless current_user.admin?
end
end
# app/controllers/admin/products_controller.rb
class Admin::ProductsController < AdminController
# This controller will inherit :authenticate_admin_user! filter
end
Attention, dans Rails 4.x, vous pouvez utiliser before_filter
avec before_action
, mais before_filter
est actuellement obsolète dans Rails 5.0.0 et sera supprimé dans 5.1 .
Générer un contrôleur
Rails fournit beaucoup de générateurs, pour les contrôleurs aussi bien sûr.
Vous pouvez générer un nouveau contrôleur en exécutant cette commande dans le dossier de votre application.
rails generate controller NAME [action action] [options]
Remarque: Vous pouvez également utiliser des rails g
alias pour appeler des rails generate
Par exemple, pour générer un contrôleur pour un modèle de Product
, #show
actions #index
et #show
rails generate controller products index show
Cela va créer le contrôleur dans app/controllers/products_controller.rb
, avec les deux actions que vous avez spécifiées
class ProductsController < ApplicationController
def index
end
def show
end
end
Il créera également un dossier de products
dans app/views/
, contenant les deux modèles pour les actions de votre contrôleur (c.-à-d index.html.erb
show.html.erb
et show.html.erb
, notez que l’extension peut varier selon votre moteur de template, donc si vous utilise slim
, par exemple, le générateur va créer index.html.slim
et show.html.slim
)
De plus, si vous avez spécifié des actions, elles seront également ajoutées à votre fichier de routes
.
# config/routes.rb
get 'products/show'
get 'products/index'
Rails crée un fichier d'aide dans app/helpers/products_helper.rb
, ainsi que les fichiers de ressources dans app/assets/javascripts/products.js
et app/assets/stylesheets/products.css
. En ce qui concerne les vues, le générateur modifie ce comportement en fonction de ce qui est spécifié dans votre Gemfile
: par exemple, si vous utilisez Coffeescript
et Sass
dans votre application, le générateur de contrôleur Coffeescript
plutôt products.coffee
et products.sass
.
Enfin, Rails génère des fichiers de test pour votre contrôleur, votre assistant et vos vues.
Si vous ne voulez pas que l'un de ces éléments soit créé, vous pouvez demander à Rails de les ignorer.
--no-
ou --skip
, comme ceci:
rails generate controller products index show --no-assets --no-helper
Et le générateur ignorera les assets
et les helper
Si vous devez créer un contrôleur pour un namespace
spécifique, ajoutez-le devant NAME
:
rails generate controller admin/products
Cela va créer votre contrôleur dans app/controllers/admin/products_controller.rb
Rails peut également générer un contrôleur RESTful complet pour vous:
rails generate scaffold_controller MODEL_NAME # available from Rails 4
rails generate scaffold_controller Product
Sauver ActiveRecord :: RecordNotFound avec redirect_to
Vous pouvez récupérer une exception RecordNotFound avec une redirection au lieu d'afficher une page d'erreur:
class ApplicationController < ActionController::Base
# your other stuff
rescue_from ActiveRecord::RecordNotFound do |exception|
redirect_to root_path, 404, alert: I18n.t("errors.record_not_found")
end
end