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 contenu Hello 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


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow