Zoeken…


Invoering

Action Controller is de C in MVC. Nadat de router heeft bepaald welke controller voor een aanvraag moet worden gebruikt, is de controller verantwoordelijk voor het begrijpen van de aanvraag en het produceren van de uitvoer.

De controller zal het verzoek ontvangen, gegevens ophalen of opslaan van een model en een weergave gebruiken om uitvoer te maken. Een controller kan worden beschouwd als een tussenpersoon tussen modellen en weergaven. Het maakt de modelgegevens beschikbaar voor de weergave, zodat deze aan de gebruiker kunnen worden weergegeven, en het slaat gebruikersgegevens op of werkt deze bij naar het model.

Voer JSON uit in plaats van 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

Daarnaast heeft u de route nodig:

resources :users, only: [:index]

Dit zal op twee verschillende manieren reageren op verzoeken van /users :

  • Als u /users of /users.html , wordt een html-pagina weergegeven met de inhoud Hello World
  • Als u /users.json bezoekt, wordt een JSON-object weergegeven met:
[
  {
    "name": "foo",
    "email": "[email protected]"
  }
]

U kunt format.html { render inline: "Hello World" } weglaten als u zeker wilt weten dat uw route alleen op JSON-verzoeken reageert.

Controllers (basis)

class UsersController < ApplicationController
  def index
    respond_to do |format|
      format.html { render html: "Hello World" }
    end
  end
end

Dit is een basiscontroller, met de toevoeging van de volgende route (in routes.rb):

resources :users, only: [:index]

Toont het Hello World bericht op een webpagina wanneer u de URL /users

parameters

Controllers hebben toegang tot HTTP-parameters (u kent ze misschien als ?name=foo in URL's, maar Ruby on Rails hanteren ook verschillende indelingen!) En voeren op basis daarvan verschillende antwoorden uit. Er is geen manier om onderscheid te maken tussen GET- en POST-parameters, maar dat moet u in elk geval niet doen.

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

Zoals gewoonlijk onze route:

resources :users, only: [:index]

Toegang tot de URL /users?name=john en de output zal Hello John , toegang /users?name=whatever en de output zal Hello someone

Filterparameters (basis)

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

U kunt sommige params toestaan (of weigeren), zodat alleen wat u wilt passeren en u niet voor verrassingen komt te staan, zoals opties voor gebruikersinstellingen die niet zijn bedoeld om te worden gewijzigd.

Bezoeken /users?name=john&sentence=developer zal Hello john developer , maar bezoekende /users?name=smith&sentence=spy zal alleen Hello smith , omdat :sentence alleen is toegestaan als je als john toegang hebt

Redirecting

Uitgaande van de route:

resources :users, only: [:index]

U kunt omleiden naar een andere URL met:

class UsersController
  def index
    redirect_to "http://stackoverflow.com/"
  end
end

U kunt teruggaan naar de vorige pagina die de gebruiker heeft bezocht met:

redirect_to :back

Merk op dat in Rails 5 de syntaxis voor het terugleiden anders is:

redirect_back fallback_location: "http://stackoverflow.com/"

Die zal proberen om te leiden naar de vorige pagina en in het geval dat niet mogelijk is (de browser blokkeert de HTTP_REFERRER-header), zal deze omleiden naar :fallback_location

Weergaven gebruiken

Uitgaande van de route:

resources :users, only: [:index]

En de controller:

class UsersController < ApplicationController
  def index
    respond_to do |format|
      format.html { render }
    end
  end
end

De view- app/users/index.html.erb wordt weergegeven. Als het uitzicht is:

Hello <strong>World</strong>

De uitvoer zal een webpagina zijn met de tekst: "Hallo wereld "

Als u een andere weergave wilt renderen, kunt u het volgende gebruiken:

render "pages/home"

En in plaats daarvan wordt het bestand app/views/pages/home.html.erb gebruikt.

U kunt variabelen doorgeven aan weergaven met behulp van controllerinstantievariabelen:

class UsersController < ApplicationController
  def index
    @name = "john"

    respond_to do |format|
      format.html { render }
    end
  end
end

En in het bestand app/views/users/index.html.erb kunt u @name :

Hello <strong><%= @name %></strong>

En de output zal zijn: "Hallo John "

Een belangrijke opmerking over de te maken syntax, kun je weglaten het render syntax geheel, Rails gaat ervan uit dat als je het weglaten. Zo:

class UsersController < ApplicationController
  def index
    respond_to do |format|
      format.html { render }
    end
  end
end

Kan in plaats daarvan worden geschreven als:

class UsersController < ApplicationController
  def index
    respond_to do |format|
      format.html
    end
  end
end

Rails is slim genoeg om erachter te komen dat het het bestand app/views/users/index.html.erb .

404 wanneer record niet gevonden

Redding van record niet gevonden fout in plaats van het tonen van een uitzondering of witte pagina:

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

Basic REST-controller

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

Foutpagina's weergeven voor uitzonderingen

Als u zinvolle fouten aan uw gebruikers wilt weergeven in plaats van eenvoudig "sorry, er is iets misgegaan", heeft Rails hiervoor een leuke tool.

Open de file app/controllers/application_controller.rb en je zou zoiets als dit moeten vinden:

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
end

We kunnen nu een rescue_from om te herstellen van specifieke fouten:

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

Het wordt aanbevolen om niet te redden van Exception of StandardError anders kan Rails geen nuttige pagina's weergeven in geval van fouten.

filters

Filters zijn methoden die "vóór", "na" of "rond" een controlleractie worden uitgevoerd. Ze zijn geërfd, dus als u deze in uw ApplicationController instelt, worden ze uitgevoerd voor elke aanvraag die uw toepassing ontvangt.

Voor filter

Voordat filters worden uitgevoerd voordat de controller actie uitvoert en het verzoek kan stoppen (en / of omleiden). Een veelgebruikt gebruik is om te controleren of een gebruiker is ingelogd:

class ApplicationController < ActionController::Base
  before_action :authenticate_user!

  def authenticate_user!
    redirect_to some_path unless user_signed_in?
  end
end

Voordat filters worden uitgevoerd op aanvragen voordat de aanvraag de actie van de controller krijgt. Het kan zelf een reactie retourneren en de actie volledig omzeilen.

Andere veel voorkomende toepassingen van filters vóór is het valideren van de authenticatie van een gebruiker voordat deze toegang krijgt tot de actie die is aangewezen om hun verzoek af te handelen. Ik heb ze ook zien worden gebruikt om een bron uit de database te laden, machtigingen voor een bron te controleren of omleidingen onder andere omstandigheden te beheren.

Na filter

Na filters zijn vergelijkbaar met "voor" degenen, maar als ze worden uitgevoerd na de actie uitvoeren, hebben ze toegang tot het reactieobject dat wordt verzonden. Dus kort nadat filters zijn uitgevoerd nadat de actie is voltooid. Het kan het antwoord wijzigen. Meestal als er iets in een afterfilter wordt gedaan, kan dit in de actie zelf worden gedaan, maar als er enige logica moet worden uitgevoerd na het uitvoeren van een reeks acties, is een afterfilter een goede plek om te doen het.

Over het algemeen heb ik na en rond filters gezien die worden gebruikt voor logboekregistratie.

Rond filter

Rondom filters kunnen logica hebben voor en na de actie die wordt uitgevoerd. Het geeft gewoon toe aan de actie op de plaats waar dat nodig is. Merk op dat het niet hoeft over te geven aan de actie en kan worden uitgevoerd zonder dit te doen zoals een voorfilter.

Around-filters zijn verantwoordelijk voor het uitvoeren van hun bijbehorende acties door te geven, vergelijkbaar met hoe Rack-middlewares werken.

Rond callbacks lopen de uitvoering van acties. U kunt een rond callback in twee verschillende stijlen schrijven. In de eerste is de callback een stuk code. Die code wordt aangeroepen voordat de actie wordt uitgevoerd. Als de callback-code opbrengst oproept, wordt de actie uitgevoerd. Wanneer de actie is voltooid, wordt de terugbelcode uitgevoerd. De code vóór de opbrengst is dus als een callback voor actie en de code na de opbrengst is de callback na actie. Als de callback-code nooit opbrengst oproept. de actie wordt niet uitgevoerd - dit is hetzelfde als het hebben van een voor actie-callback return false.

Hier is een voorbeeld van het rondfilter:

around_filter :catch_exceptions
 
private
  def catch_exceptions
    begin
      yield
    rescue Exception => e 
      logger.debug "Caught exception! #{e.message}"
    end
  end

Hiermee wordt elke actie uitgesloten en wordt het bericht in uw logboek geplaatst. U kunt filters gebruiken voor het afhandelen van uitzonderingen, instellen en verwijderen, en een groot aantal andere gevallen.

Alleen en behalve

Alle filters kunnen worden toegepast op specifieke acties, met behulp van :only en :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

Overslaan van filter

Alle filters (ook overgeërfde) kunnen ook worden overgeslagen voor enkele specifieke acties:

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

Omdat ze worden geërfd, kunnen filters ook worden gedefinieerd in een namespace . Stel bijvoorbeeld dat u een admin naamruimte hebt en dat u natuurlijk alleen admin-gebruikers toegang wilt geven. Je zou zoiets kunnen doen:

# 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

Let op: in Rails 4.x kunt u before_filter samen met before_action , maar before_filter is momenteel verouderd in Rails 5.0.0 en wordt verwijderd in 5.1 .

Een controller genereren

Rails biedt veel generators, ook voor controllers natuurlijk.

U kunt een nieuwe controller genereren door deze opdracht in uw app-map uit te voeren

rails generate controller NAME [action action] [options]

Opmerking: u kunt ook rails g alias gebruiken om rails generate

Om bijvoorbeeld een controller voor een Product te genereren, met #index en #show acties die u zou uitvoeren

rails generate controller products index show

Hiermee wordt de controller gemaakt in app/controllers/products_controller.rb , met beide acties die u hebt opgegeven

class ProductsController < ApplicationController
  def index
  end

  def show
  end
end

Het zal ook zorgen voor een products map in app/views/ , die de twee sjablonen voor de acties van de controller (dwz index.html.erb en show.html.erb , er rekening mee dat de uitbreiding kan variëren afhankelijk van uw template engine, dus als je gebruikt je bijvoorbeeld slim , dan maakt de generator index.html.slim en show.html.slim )

Als u acties hebt opgegeven, worden deze ook toegevoegd aan uw routes

# config/routes.rb
get 'products/show'
get 'products/index'

Rails maakt een helperbestand voor u, in app/helpers/products_helper.rb , en ook de activabestanden in app/assets/javascripts/products.js en app/assets/stylesheets/products.css . Wat de weergaven betreft, wijzigt de generator dit gedrag volgens wat is gespecificeerd in uw Gemfile : dat wil zeggen, als u Coffeescript en Sass in uw toepassing gebruikt, Coffeescript de controllergenerator in plaats daarvan products.coffee en products.sass .

Eindelijk, maar daarom niet minder belangrijk, genereert Rails ook testbestanden voor uw controller, uw helper en uw weergaven.

Als je niet wilt dat een van deze wordt gemaakt, kun je Rails vertellen om ze over te slaan

--no- of --skip , zoals dit:

rails generate controller products index show --no-assets --no-helper

En de generator slaat zowel assets als helper

Als u een controller voor een specifieke namespace voegt u deze toe voor NAME :

rails generate controller admin/products

Hiermee maakt u uw controller in de app/controllers/admin/products_controller.rb

Rails kan ook een complete RESTful-controller voor u genereren:

rails generate scaffold_controller MODEL_NAME # available from Rails 4
rails generate scaffold_controller Product

ActiveRecord redden: RecordNotFound met redirect_to

U kunt een RecordNotFound-uitzondering redden met een omleiding in plaats van een foutpagina weer te geven:

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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow