Поиск…


Вступление

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


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow