Ruby on Rails
ActionController
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 inhoudHello 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