Ruby on Rails
ActionController
Szukaj…
Wprowadzenie
Kontroler akcji to C w MVC. Po tym, jak router określi, którego kontrolera użyć do żądania, kontroler jest odpowiedzialny za zrozumienie żądania i wygenerowanie danych wyjściowych.
Kontroler otrzyma żądanie, pobierze lub zapisze dane z modelu i użyje widoku do utworzenia danych wyjściowych. Kontroler może być uważany za pośrednika między modelami a widokami. Udostępnia dane modelu w widoku, dzięki czemu można je wyświetlić użytkownikowi, a także zapisuje lub aktualizuje dane użytkownika w modelu.
Wyjście JSON zamiast 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
Ponadto będziesz potrzebować trasy:
resources :users, only: [:index]
To zareaguje na dwa różne sposoby na żądania od /users
:
- Jeśli odwiedzisz
/users
lub/users.html
, wyświetli się strona HTML z treściąHello World
- Jeśli odwiedzisz
/users.json
, wyświetli obiekt JSON zawierający:
[ { "name": "foo", "email": "[email protected]" } ]
Możesz pominąć format.html { render inline: "Hello World" }
jeśli chcesz się upewnić, że twoja trasa będzie odpowiadać tylko na żądania JSON.
Kontrolery (podstawowe)
class UsersController < ApplicationController def index respond_to do |format| format.html { render html: "Hello World" } end end end
Jest to podstawowy kontroler, z dodatkiem następującej trasy (w route.rb):
resources :users, only: [:index]
Wyświetli komunikat Hello World
na stronie internetowej, gdy uzyskasz dostęp do adresu URL /users
Parametry
Kontrolery mają dostęp do parametrów HTTP (możesz znać je jako ?name=foo
w adresach URL, ale Ruby on Rails też obsługuje różne formaty!) I na ich podstawie generują różne odpowiedzi. Nie ma sposobu na rozróżnienie parametrów GET i POST, ale w żadnym wypadku nie powinieneś tego robić.
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
Jak zwykle nasza trasa:
resources :users, only: [:index]
Uzyskaj dostęp do adresu URL /users?name=john
a wynikiem będzie Hello John
, access /users?name=whatever
a wynik będzie Hello someone
Parametry filtrowania (podstawowe)
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
Możesz zezwolić (lub odrzucić) niektóre parametry, aby przejść tylko to, co chcesz, i nie będziesz mieć złych niespodzianek, takich jak opcje ustawień użytkownika, których nie należy zmieniać.
Odwiedzający /users?name=john&sentence=developer
wyświetli Hello john developer
, jednak odwiedzający /users?name=smith&sentence=spy
wyświetli tylko Hello smith
, ponieważ :sentence
jest dozwolone tylko wtedy, gdy masz dostęp jako john
Przekierowanie
Zakładając trasę:
resources :users, only: [:index]
Możesz przekierować na inny adres URL, używając:
class UsersController def index redirect_to "http://stackoverflow.com/" end end
Możesz wrócić do poprzedniej strony, którą odwiedził użytkownik, używając:
redirect_to :back
Zauważ, że w Railsach 5 składnia przekierowywania z powrotem jest inna:
redirect_back fallback_location: "http://stackoverflow.com/"
Który spróbuje przekierować na poprzednią stronę, a jeśli nie będzie to możliwe (przeglądarka blokuje nagłówek HTTP_REFERRER), przekieruje do :fallback_location
Korzystanie z widoków
Zakładając trasę:
resources :users, only: [:index]
I kontroler:
class UsersController < ApplicationController def index respond_to do |format| format.html { render } end end end
Zostanie wyświetlona app/users/index.html.erb
view app/users/index.html.erb
. Jeśli widok jest:
Hello <strong>World</strong>
Wyjściem będzie strona internetowa z tekstem: „Hello World ”
Jeśli chcesz wyrenderować inny widok, możesz użyć:
render "pages/home"
Zamiast tego zostanie użyta app/views/pages/home.html.erb
.
Zmienne można przekazywać do widoków przy użyciu zmiennych instancji kontrolera:
class UsersController < ApplicationController def index @name = "john" respond_to do |format| format.html { render } end end end
W app/views/users/index.html.erb
file app/views/users/index.html.erb
możesz użyć @name
:
Hello <strong><%= @name %></strong>
A wynik będzie następujący: „Hello John ”
Ważna uwaga dotycząca składni renderowania, możesz całkowicie pominąć składnię render
, Railsy zakładają, że jeśli ją pominiesz. Więc:
class UsersController < ApplicationController def index respond_to do |format| format.html { render } end end end
Zamiast tego można zapisać jako:
class UsersController < ApplicationController def index respond_to do |format| format.html end end end
Railsy są na tyle sprytne, by zorientować się, że muszą renderować app/views/users/index.html.erb
.
404 gdy rekord nie został znaleziony
Błąd ratowania z rekordu zamiast pokazywania wyjątku lub białej strony:
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
Podstawowy kontroler REST
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
Wyświetl strony błędów dla wyjątków
Jeśli chcesz wyświetlać użytkownikom znaczące błędy zamiast prostego „przepraszam, coś poszło nie tak”, Rails ma do tego przydatne narzędzie.
Otwórz plik app/controllers/application_controller.rb
i powinieneś znaleźć coś takiego:
class ApplicationController < ActionController::Base protect_from_forgery with: :exception end
Możemy teraz dodać rescue_from
aby odzyskać dane błędy:
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
Zaleca się, aby nie ratować przed Exception
lub błędami StandardError
przeciwnym razie Railsy nie będą mogły wyświetlać pomocnych stron w przypadku błędów.
Filtry
Filtry to metody uruchamiane „przed”, „po” lub „wokół” akcji kontrolera. Są dziedziczone, więc jeśli ustawisz dowolną wartość w ApplicationController
, będą one uruchamiane dla każdego żądania otrzymanego przez aplikację.
Przed filtrem
Przed wykonaniem filtrów przed działaniem kontrolera i może zatrzymać żądanie (i / lub przekierować). Typowym zastosowaniem jest sprawdzenie, czy użytkownik jest zalogowany:
class ApplicationController < ActionController::Base
before_action :authenticate_user!
def authenticate_user!
redirect_to some_path unless user_signed_in?
end
end
Przed uruchomieniem filtrów dla żądań, zanim żądanie przejdzie do działania kontrolera. Może zwrócić odpowiedź i całkowicie ominąć akcję.
Inne typowe zastosowania filtrów przed sprawdzaniem poprawności uwierzytelnienia użytkownika przed przyznaniem mu dostępu do działania wyznaczonego do obsługi jego żądania. Widziałem też, jak były używane do ładowania zasobu z bazy danych, sprawdzania uprawnień do zasobu lub zarządzania przekierowaniami w innych okolicznościach.
Po filtrze
Po tym, jak filtry są podobne do „przed”, ale gdy zostaną wykonane po uruchomieniu akcji, mają dostęp do obiektu odpowiedzi, który ma zostać wysłany. Krótko po uruchomieniu filtrów po zakończeniu akcji. Może modyfikować odpowiedź. Przez większość czasu, jeśli coś jest zrobione w filtrze końcowym, można to zrobić w samej akcji, ale jeśli istnieje pewna logika do uruchomienia po uruchomieniu któregokolwiek z zestawu akcji, wtedy filtr końcowy jest dobrym miejscem do zrobienia to.
Ogólnie widziałem filtry używane do rejestrowania i wokół nich.
Wokół filtra
Wokół filtrów może występować logika przed uruchomieniem akcji i po niej. Po prostu ulega akcji w dowolnym miejscu. Zauważ, że nie musi on ulegać akcji i może działać bez robienia tego jak filtr przed.
Wokół filtrów są odpowiedzialne za uruchamianie powiązanych z nimi działań przez tworzenie, podobnie jak w przypadku pośrednich programów typu Rack.
Wokół wywołań zwrotnych zawijaj wykonywanie akcji. Możesz napisać oddzwonienie w dwóch różnych stylach. W pierwszym wywołanie zwrotne jest pojedynczą porcją kodu. Ten kod jest wywoływany przed wykonaniem akcji. Jeśli kod wywołania zwrotnego wywołuje wydajność, akcja jest wykonywana. Po zakończeniu akcji kod wywołania zwrotnego jest kontynuowany. Zatem kod przed wydajnością jest jak wywołanie zwrotne przed działaniem, a kod po wydajności jest wywołaniem zwrotnym po akcji. Jeśli kod wywołania zwrotnego nigdy nie wywołuje wydajności. akcja nie jest uruchamiana - jest to to samo, co zwrot false przed wywołaniem zwrotnym przed akcją.
Oto przykład filtra dookoła:
around_filter :catch_exceptions
private
def catch_exceptions
begin
yield
rescue Exception => e
logger.debug "Caught exception! #{e.message}"
end
end
Spowoduje to wychwycenie wyjątku dowolnej akcji i umieszczenie wiadomości w dzienniku. Możesz korzystać z filtrów w celu obsługi wyjątków, konfiguracji i porzucania oraz wielu innych przypadków.
Tylko i z wyjątkiem
Wszystkie filtry można zastosować do określonych akcji, używając :only
i :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
Pomijanie filtrów
Wszystkie filtry (również odziedziczone) można również pominąć w przypadku niektórych określonych działań:
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
Gdy są dziedziczone, filtry można również zdefiniować w kontrolerze „nadrzędnym” namespace
. Powiedz na przykład, że masz admin
przestrzeń nazw i oczywiście chcesz, aby tylko użytkownicy administracyjni mieli do niej dostęp. Możesz zrobić coś takiego:
# 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
Pamiętaj, że w Rails 4.x można użyć before_filter
wraz z before_action
, ale before_filter
obecnie przestarzałe w Rails 5.0.0 i zostaną usunięte w 5.1.
Generowanie kontrolera
Railsy zapewniają wiele generatorów, oczywiście także dla kontrolerów.
Możesz wygenerować nowy kontroler, uruchamiając to polecenie w folderze aplikacji
rails generate controller NAME [action action] [options]
Uwaga: Możesz także użyć aliasu rails g
railsów, aby wywołać rails generate
Na przykład, aby wygenerować kontroler dla modelu Product
, z #show
działaniami #index
i #show
rails generate controller products index show
Spowoduje to utworzenie kontrolera w app/controllers/products_controller.rb
, z obiema określonymi przez ciebie działaniami
class ProductsController < ApplicationController
def index
end
def show
end
end
Będzie on również stworzyć products
folderów wewnątrz app/views/
, zawierający dwa szablony do działania kontrolera (tj index.html.erb
i show.html.erb
pamiętać, że rozszerzenie może się różnić w zależności od silnika szablonów, więc jeśli używa slim
, na przykład generator utworzy index.html.slim
i show.html.slim
)
Ponadto, jeśli określisz jakieś działania, zostaną one również dodane do pliku routes
# config/routes.rb
get 'products/show'
get 'products/index'
Railsy tworzą plik pomocnika dla Ciebie w app/helpers/products_helper.rb
, a także pliki zasobów w app/assets/javascripts/products.js
i app/assets/stylesheets/products.css
. Jeśli chodzi o widoki, generator zmienia to zachowanie w zależności, co określono w Gemfile
IE, jeśli używasz Coffeescript
i Sass
w aplikacji, generator regulator zamiast generatora products.coffee
i products.sass
.
Wreszcie Railsy generują również pliki testowe dla kontrolera, pomocnika i twoich widoków.
Jeśli nie chcesz, aby któryś z nich był tworzony, możesz powiedzieć Railsom, aby je pomijały, po prostu wstaw dowolną opcję za pomocą
--no-
lub --skip
, jak to:
rails generate controller products index show --no-assets --no-helper
Generator pominie zarówno assets
jak i helper
Jeśli chcesz utworzyć kontroler dla określonej namespace
dodaj go przed NAME
:
rails generate controller admin/products
Spowoduje to utworzenie kontrolera w app/controllers/admin/products_controller.rb
Railsy mogą również wygenerować dla ciebie kompletny kontroler RESTful:
rails generate scaffold_controller MODEL_NAME # available from Rails 4
rails generate scaffold_controller Product
Uratowanie ActiveRecord :: RecordNotFound za pomocą redirect_to
Możesz uratować wyjątek RecordNotFound za pomocą przekierowania zamiast wyświetlania strony błędu:
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