Ruby on Rails
ActionController
Поиск…
Вступление
Action Controller - это C в MVC. После того, как маршрутизатор определил, какой контроллер использовать для запроса, контроллер отвечает за определение запроса и вывод результата.
Контроллер получит запрос, выборку или сохранение данных из модели и использование представления для создания вывода. Контроллер можно рассматривать как посредника между моделями и представлениями. Это делает данные модели доступными для представления, чтобы он мог отображаться пользователю, и он сохраняет или обновляет данные пользователя в модели.
Вывод JSON вместо 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
Кроме того, вам понадобится маршрут:
resources :users, only: [:index]
Это будет реагировать двумя разными способами на запросы /users
:
- Если вы посетили
/users
или/users.html
, он покажет html-страницу с контентомHello World
- Если вы посетите
/users.json
, он отобразит объект JSON, содержащий:
[ { "name": "foo", "email": "[email protected]" } ]
Вы можете опустить format.html { render inline: "Hello World" }
если хотите, чтобы ваш маршрут отвечал только на запросы JSON.
Контроллеры (базовые)
class UsersController < ApplicationController def index respond_to do |format| format.html { render html: "Hello World" } end end end
Это базовый контроллер с добавлением следующего маршрута (в routes.rb):
resources :users, only: [:index]
Будет отображаться сообщение Hello World
на веб-странице при доступе к URL /users
параметры
Контроллеры имеют доступ к параметрам HTTP (вы можете знать их как ?name=foo
в URL-адресах, но Ruby on Rails обрабатывают и другие форматы!) И выводят на них разные ответы. Невозможно различать параметры GET и POST, но вы не должны этого делать в любом случае.
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
Как обычно, наш маршрут:
resources :users, only: [:index]
Получите доступ к URL /users?name=john
и на выходе будет Hello John
, access /users?name=whatever
и вывод будет Hello someone
Параметры фильтрации (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
Вы можете разрешить (или отклонить) некоторые параметры, чтобы пройти только то, что вы хотите, и у вас не будет таких неприятных сюрпризов, как параметры пользовательских настроек, которые не должны быть изменены.
Visiting /users?name=john&sentence=developer
покажет Hello john developer
, однако посещение /users?name=smith&sentence=spy
отобразит только Hello smith
, потому что :sentence
разрешено только при доступе к john
Перенаправление
Предполагая маршрут:
resources :users, only: [:index]
Вы можете перенаправить на другой URL-адрес, используя:
class UsersController def index redirect_to "http://stackoverflow.com/" end end
Вы можете вернуться к предыдущей странице, которую посетил пользователь, используя:
redirect_to :back
Обратите внимание, что в Rails 5 синтаксис для перенаправления назад отличается:
redirect_back fallback_location: "http://stackoverflow.com/"
Что будет пытаться перенаправить на предыдущую страницу, и в случае, если это невозможно (браузер блокирует заголовок HTTP_REFERRER), он перенаправляется на :fallback_location
Использование представлений
Предполагая маршрут:
resources :users, only: [:index]
И контроллер:
class UsersController < ApplicationController def index respond_to do |format| format.html { render } end end end
Будет app/users/index.html.erb
представление app/users/index.html.erb
. Если вид:
Hello <strong>World</strong>
Результатом будет веб-страница с текстом: «Hello World »
Если вы хотите отобразить другое представление, вы можете использовать:
render "pages/home"
app/views/pages/home.html.erb
этого будет использовано app/views/pages/home.html.erb
.
Вы можете передавать переменные в представления с использованием переменных экземпляра контроллера:
class UsersController < ApplicationController def index @name = "john" respond_to do |format| format.html { render } end end end
А в файле app/views/users/index.html.erb
вы можете использовать @name
:
Hello <strong><%= @name %></strong>
И выход будет: «Hello john »
Важное примечание вокруг синтаксиса рендеринга, вы можете полностью опустить синтаксис render
, Rails предполагает, что если вы его опустите. Так:
class UsersController < ApplicationController def index respond_to do |format| format.html { render } end end end
Вместо этого можно написать:
class UsersController < ApplicationController def index respond_to do |format| format.html end end end
Rails достаточно умен, чтобы понять, что он должен отображать файл app/views/users/index.html.erb
.
404, когда запись не найдена
Rescue from record not found error вместо того, чтобы показывать исключение или белую страницу:
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
Базовый контроллер 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
Отображать страницы ошибок для исключений
Если вы хотите показать своим пользователям значимые ошибки вместо простого «извините, что-то пошло не так», у Rails есть хорошая утилита для этой цели.
Откройте файл app/controllers/application_controller.rb
и вы должны найти что-то вроде этого:
class ApplicationController < ActionController::Base protect_from_forgery with: :exception end
Теперь мы можем добавить rescue_from
для восстановления от определенных ошибок:
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
Рекомендуется не извлекать из Exception
или StandardError
иначе Rails не сможет отображать полезные страницы в случае ошибок.
фильтры
Фильтры - это методы, которые запускаются «до», «после» или «вокруг» действия контроллера. Они наследуются, поэтому, если вы установите их в свой ApplicationController
они будут выполняться для каждого запроса, получаемого вашим приложением.
Перед фильтром
Прежде чем фильтры будут выполнены до действия контроллера и могут остановить запрос (и / или перенаправить). Обычно используется проверка, вошел ли пользователь в систему:
class ApplicationController < ActionController::Base
before_action :authenticate_user!
def authenticate_user!
redirect_to some_path unless user_signed_in?
end
end
Прежде чем фильтры будут запускаться по запросам до того, как запрос поступит в действие контроллера. Он может вернуть ответ сам и полностью обойти действие.
Другие распространенные применения перед фильтрами проверяют аутентификацию пользователя перед тем, как предоставить им доступ к действию, предназначенному для обработки их запроса. Я также видел, что они использовали для загрузки ресурса из базы данных, проверки разрешений на ресурс или управления переадресацией при других обстоятельствах.
После фильтра
После того, как фильтры похожи на «раньше», но по мере их запуска после запуска действия они имеют доступ к объекту ответа, который будет отправлен. Итак, коротко после того, как фильтры запускаются после завершения действия. Он может изменить ответ. В большинстве случаев, если что-то происходит в фильтре after, это можно сделать в самом действии, но если есть некоторая логика, которая будет запущена после запуска любого из множества действий, то фильтр после этого является хорошим местом для выполнения Это.
Как правило, я видел фильтры и фильтры, используемые для ведения журнала.
Фильтр вокруг
Вокруг фильтров может быть логика до и после запуска действия. Он просто уступает действию в любом месте. Обратите внимание, что он не должен уступать действию и может работать без этого, как перед фильтром.
Вокруг фильтров отвечает за выполнение своих связанных действий, уступая, подобно тому, как работают средние стойки.
Вокруг обратных вызовов завершается выполнение действий. Вы можете написать обратный вызов в двух разных стилях. В первом случае обратный вызов представляет собой единый фрагмент кода. Этот код вызывается до того, как действие будет выполнено. Если код обратного вызова вызывает выход, действие выполняется. Когда действие завершается, код обратного вызова продолжает выполняться. Таким образом, код перед выходом выглядит как обратный вызов до действия, а код после выхода - обратный вызов после действия. Если код обратного вызова никогда не вызывает доходность. действие не запускается - это то же самое, что иметь перед обратным вызовом перед возвратом false.
Вот пример фильтра вокруг:
around_filter :catch_exceptions
private
def catch_exceptions
begin
yield
rescue Exception => e
logger.debug "Caught exception! #{e.message}"
end
end
Это исключает любое действие и помещает сообщение в ваш журнал. Вы можете использовать фильтры для обработки исключений, настройки и удаления, а также множество других случаев.
Только и кроме
Все фильтры могут применяться к определенным действиям, используя :only
и :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
Пропускающий фильтр
Все фильтры (унаследованные) также могут быть пропущены для определенных действий:
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
Поскольку они унаследованы, фильтры также могут быть определены в «родительском» контроллере namespace
. Скажем, например, что у вас есть пространство имен для admin
, и вы, конечно, хотите, чтобы только пользователи-администраторы могли получить к нему доступ. Вы могли бы сделать что-то вроде этого:
# 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
Помните, что в Rails 4.x вы можете использовать before_filter
вместе с before_action
, но before_filter
в настоящее время устарел в Rails 5.0.0 и будет удален в 5.1 .
Создание контроллера
Rails предоставляет множество генераторов, конечно же, для контроллеров.
Вы можете создать новый контроллер, выполнив эту команду в папке приложения
rails generate controller NAME [action action] [options]
Примечание. Вы также можете использовать псевдонимы rails g
для вызова rails generate
Например, чтобы создать контроллер для модели Product
, с действиями #index
и #show
вы #show
бы
rails generate controller products index show
Это создаст контроллер в app/controllers/products_controller.rb
, причем оба указанных вами действия
class ProductsController < ApplicationController
def index
end
def show
end
end
Он также создаст папку products
внутри app/views/
, содержащую два шаблона для действий вашего контроллера (например, index.html.erb
и show.html.erb
, обратите внимание, что расширение может варьироваться в зависимости от вашего шаблона, поэтому, если вы Например, при использовании slim
, генератор создаст index.html.slim
и show.html.slim
)
Кроме того, если вы указали какие-либо действия, они также будут добавлены в файл routes
# config/routes.rb
get 'products/show'
get 'products/index'
Rails создает вспомогательный файл для вас, в app/helpers/products_helper.rb
, а также файлы ресурсов в app/assets/javascripts/products.js
и app/assets/stylesheets/products.css
. Что касается представлений, генератор изменяет это поведение в соответствии с тем, что указано в вашем Gemfile
: т. Gemfile
Если вы используете Coffeescript
и Sass
в своем приложении, генератор контроллера будет вместо этого генерировать products.coffee
и products.sass
.
Наконец, но не в последнюю очередь, Rails также создает тестовые файлы для вашего контроллера, вашего помощника и ваших просмотров.
Если вы не хотите, чтобы какой-либо из них был создан, вы можете сказать, что Rails пропустили их, просто добавьте любую опцию с помощью
--no-
или --skip
, вот так:
rails generate controller products index show --no-assets --no-helper
И генератор пропустит оба assets
и helper
Если вам нужно создать контроллер для определенного namespace
добавьте его перед NAME
:
rails generate controller admin/products
Это создаст ваш контроллер внутри app/controllers/admin/products_controller.rb
Rails также может создать для вас полный контроллер RESTful:
rails generate scaffold_controller MODEL_NAME # available from Rails 4
rails generate scaffold_controller Product
Rescuing ActiveRecord :: RecordNotFound с redirect_to
Вы можете спасти исключение RecordNotFound с помощью перенаправления вместо отображения страницы с ошибкой:
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