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


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow