Ricerca…


introduzione

Action Controller è la C in MVC. Dopo che il router ha determinato quale controller utilizzare per una richiesta, il controller è responsabile di dare un senso alla richiesta e produrre l'output.

Il controller riceverà la richiesta, recupera o salva i dati da un modello e utilizza una vista per creare output. Un controller può essere pensato come un intermediario tra modelli e viste. Rende i dati del modello disponibili alla vista in modo che possano essere visualizzati dall'utente e salva o aggiorna i dati dell'utente sul modello.

Output JSON anziché 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

Inoltre avrai bisogno del percorso:

resources :users, only: [:index]

Ciò risponderà in due modi diversi alle richieste su /users :

  • Se visiti /users o /users.html , mostrerà una pagina html con il contenuto Hello World
  • Se visiti /users.json , verrà visualizzato un oggetto JSON contenente:
[
  {
    "name": "foo",
    "email": "[email protected]"
  }
]

Puoi omettere format.html { render inline: "Hello World" } se vuoi assicurarti che il tuo percorso risponda solo alle richieste JSON.

Controller (base)

class UsersController < ApplicationController
  def index
    respond_to do |format|
      format.html { render html: "Hello World" }
    end
  end
end

Questo è un controller di base, con l'aggiunta del seguente percorso (in routes.rb):

resources :users, only: [:index]

Visualizzerà il messaggio Hello World in una pagina Web quando si accede all'URL /users

parametri

I controllori hanno accesso ai parametri HTTP (potresti conoscerli come ?name=foo negli URL, ma Ruby on Rails gestisce anche diversi formati!) E genera risposte diverse basate su di essi. Non c'è un modo per distinguere tra i parametri GET e POST, ma in ogni caso non dovresti farlo.

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

Come al solito il nostro percorso:

resources :users, only: [:index]

Accedi all'URL /users?name=john e l'output sarà Hello John , access /users?name=whatever e l'output sarà Hello someone

Parametri di filtraggio (base)

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

Puoi consentire (o rifiutare) alcuni parametri in modo che solo quello che vuoi passi e che non abbia brutte sorprese come le opzioni di impostazione dell'utente che non devono essere modificate.

Visiting /users?name=john&sentence=developer visualizzerà Hello john developer , tuttavia visitando /users?name=smith&sentence=spy mostrerà solo Hello smith , perché :sentence è consentita solo quando accedi come john

Riorientare

Supponendo il percorso:

resources :users, only: [:index]

Puoi reindirizzare a un URL diverso usando:

class UsersController
  def index
    redirect_to "http://stackoverflow.com/"
  end
end

Puoi tornare alla pagina precedente che l'utente ha visitato utilizzando:

redirect_to :back

Nota che in Rails 5 la sintassi per il reindirizzamento è diversa:

redirect_back fallback_location: "http://stackoverflow.com/"

Che proverà a reindirizzare alla pagina precedente e nel caso non sia possibile (il browser sta bloccando l'intestazione HTTP_REFERRER), verrà reindirizzato a :fallback_location

Utilizzo di viste

Supponendo il percorso:

resources :users, only: [:index]

E il controller:

class UsersController < ApplicationController
  def index
    respond_to do |format|
      format.html { render }
    end
  end
end

L' app/users/index.html.erb visualizzazione app/users/index.html.erb sarà renderizzata. Se la vista è:

Hello <strong>World</strong>

L'output sarà una pagina web con il testo: "Hello World "

Se vuoi rendere una vista diversa, puoi usare:

render "pages/home"

Verranno invece utilizzati i file app/views/pages/home.html.erb .

È possibile passare variabili alle viste utilizzando le variabili di istanza del controllore:

class UsersController < ApplicationController
  def index
    @name = "john"

    respond_to do |format|
      format.html { render }
    end
  end
end

E nel file app/views/users/index.html.erb puoi usare @name :

Hello <strong><%= @name %></strong>

E l'output sarà: "Ciao John "

Una nota importante intorno alla sintassi del rendering, è che puoi omettere completamente la sintassi del render , Rails presume che se lo ometti. Così:

class UsersController < ApplicationController
  def index
    respond_to do |format|
      format.html { render }
    end
  end
end

Può essere scritto invece come:

class UsersController < ApplicationController
  def index
    respond_to do |format|
      format.html
    end
  end
end

Rails è abbastanza intelligente da capire che deve rendere il file app/views/users/index.html.erb .

404 quando la registrazione non è stata trovata

Salvataggio da un errore non trovato di record invece di mostrare un'eccezione o una pagina bianca:

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

Controller REST di 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

Visualizza le pagine di errore per le eccezioni

Se vuoi mostrare agli utenti errori significativi invece di "scusa, qualcosa è andato storto", Rails ha una buona utilità per lo scopo.

Apri l' app/controllers/application_controller.rb file app/controllers/application_controller.rb e dovresti trovare qualcosa di simile a questo:

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
end

Ora possiamo aggiungere un rescue_from per recuperare da errori specifici:

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

Si raccomanda di non eseguire il salvataggio da Exception o StandardError altrimenti Rails non sarà in grado di visualizzare pagine utili in caso di errori.

filtri

I filtri sono metodi che vengono eseguiti "prima", "dopo" o "attorno" a un'azione del controller. Sono ereditati, quindi se ne inserisci uno nel tuo ApplicationController verranno eseguiti per ogni richiesta ricevuta dalla tua applicazione.

Prima del filtro

Prima che i filtri vengano eseguiti prima dell'azione del controllore e possono interrompere la richiesta (e / o il reindirizzamento). Un uso comune è verificare se un utente è loggato:

class ApplicationController < ActionController::Base
  before_action :authenticate_user!

  def authenticate_user!
    redirect_to some_path unless user_signed_in?
  end
end

Prima che i filtri vengano eseguiti sulle richieste prima che la richiesta raggiunga l'azione del controllore. Può restituire una risposta e ignorare completamente l'azione.

Altri usi comuni dei filtri precedenti convalidano l'autenticazione dell'utente prima di concedere loro l'accesso all'azione designata per gestire la loro richiesta. Li ho visti anche per caricare una risorsa dal database, controllare le autorizzazioni su una risorsa o gestire i reindirizzamenti in altre circostanze.

Dopo il filtro

Dopo che i filtri sono simili a quelli "precedenti", ma quando vengono eseguiti dopo l'esecuzione dell'azione, hanno accesso all'oggetto risposta che sta per essere inviato. Quindi, in breve, i filtri vengono eseguiti al termine dell'azione. Può modificare la risposta. La maggior parte delle volte se qualcosa viene fatto in un after filter, può essere fatto nell'azione stessa, ma se c'è qualche logica da eseguire dopo aver eseguito una serie di azioni, allora un after filter è un buon posto dove fare esso.

In generale, ho visto dopo e intorno ai filtri utilizzati per la registrazione.

Intorno al filtro

Intorno ai filtri potrebbe essere presente logica prima e dopo l'esecuzione dell'azione. Si arrende semplicemente all'azione in qualunque posto sia necessario. Si noti che non ha bisogno di arrendersi all'azione e può essere eseguito senza farlo come un filtro precedente.

Intorno ai filtri è responsabile l'esecuzione delle azioni associate cedendo, analogamente a come funzionano i media di Rack.

Intorno ai callback avvolgono l'esecuzione delle azioni. Puoi scrivere un callback in due stili diversi. Nel primo, il callback è un singolo pezzo di codice. Questo codice viene chiamato prima che l'azione venga eseguita. Se il codice di richiamata richiama rendimento, l'azione viene eseguita. Al termine dell'azione, il codice di richiamata continua ad essere eseguito. Pertanto, il codice prima del rendimento è come un callback prima dell'azione e il codice dopo il rendimento è il callback dopo l'azione. Se il codice di richiamata non invoca mai il rendimento. l'azione non viene eseguita, equivale ad avere una prima azione callback return false.

Ecco un esempio del filtro in giro:

around_filter :catch_exceptions
 
private
  def catch_exceptions
    begin
      yield
    rescue Exception => e 
      logger.debug "Caught exception! #{e.message}"
    end
  end

Questo prenderà l'eccezione di qualsiasi azione e metterà il messaggio nel tuo log. È possibile utilizzare i filtri per la gestione delle eccezioni, l'installazione e la rimozione e una miriade di altri casi.

Solo ed Escluso

Tutti i filtri possono essere applicati a azioni specifiche, usando :only e :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

Saltare il filtro

Tutti i filtri (anche quelli ereditati) possono essere saltati per alcune azioni specifiche:

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

Poiché vengono ereditati, i filtri possono anche essere definiti in un controller "parent" di un namespace . Supponiamo, ad esempio, di avere uno spazio dei nomi di admin e, naturalmente, si desidera che solo gli utenti amministratori possano accedervi. Potresti fare qualcosa del genere:

# 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

before_filter attenzione che in Rails 4.x puoi usare before_filter insieme a before_action , ma before_filter è attualmente deprecato in Rails 5.0.0 e verrà rimosso in 5.1 .

Generazione di un controller

Rails fornisce molti generatori, ovviamente anche per i controller.

È possibile generare un nuovo controller eseguendo questo comando nella cartella dell'app

rails generate controller NAME [action action] [options]

Nota: è anche possibile utilizzare l'alias rails g per invocare la rails generate

Ad esempio, per generare un controller per un modello di Product , con le azioni #index e #show eseguite

rails generate controller products index show

Questo creerà il controller in app/controllers/products_controller.rb , con entrambe le azioni specificate

class ProductsController < ApplicationController
  def index
  end

  def show
  end
end

Sarà inoltre possibile creare un products cartella all'interno app/views/ , che contiene i due modelli per le azioni del controller (cioè index.html.erb e show.html.erb , notare che l'estensione può variare a seconda del motore di template, quindi se si Usando slim , ad esempio, il generatore creerà index.html.slim e show.html.slim )

Inoltre, se hai specificato azioni, verranno aggiunte al tuo file di routes

# config/routes.rb
get 'products/show'
get 'products/index'

Rails crea un file di supporto per te, in app/helpers/products_helper.rb , e anche i file delle risorse in app/assets/javascripts/products.js e app/assets/stylesheets/products.css . Per quanto riguarda le visualizzazioni, il generatore modifica questo comportamento in base a quanto specificato nel tuo Gemfile : cioè, se stai utilizzando Coffeescript e Sass nella tua applicazione, il generatore di controller genererà invece products.coffee e products.sass .

Infine, ma non meno importante, Rails genera anche file di test per il controller, l'helper e le visualizzazioni.

Se non vuoi che qualcuno di questi venga creato per te, puoi dire a Rails di saltarli, basta anteporre qualsiasi opzione a

--no- o --skip , come questo:

rails generate controller products index show --no-assets --no-helper

E il generatore salterà sia le assets che l' helper

Se devi creare un controller per uno namespace dei namespace specifico, aggiungilo davanti a NAME :

rails generate controller admin/products

Questo creerà il controller all'interno di app/controllers/admin/products_controller.rb

Rails può anche generare un controller RESTful completo per te:

rails generate scaffold_controller MODEL_NAME # available from Rails 4
rails generate scaffold_controller Product

Salvataggio di ActiveRecord :: RecordNotFound con redirect_to

Puoi salvare un'eccezione RecordNotFound con un reindirizzamento invece di mostrare una pagina di errore:

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
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow