Ruby on Rails
ActionController
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 contenutoHello 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