Ruby on Rails
ActionController
Sök…
Introduktion
Action Controller är C i MVC. Efter att routern har bestämt vilken styrenhet som ska användas för en begäran, är styrenheten ansvarig för att förstå förfrågan och producera utgången.
Styrenheten tar emot begäran, hämtar eller sparar data från en modell och använder en vy för att skapa utdata. En styrenhet kan ses som en mellanhand mellan modeller och vyer. Den gör modellinformationen tillgänglig för vyn så att den kan visas för användaren, och den sparar eller uppdaterar användardata till modellen.
Output JSON istället för 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
Dessutom behöver du rutten:
resources :users, only: [:index]
Detta kommer att svara på två olika sätt på förfrågningar till /users
:
- Om du besöker
/users
eller/users.html
kommer den att visa en html-sida med innehålletHello World
- Om du besöker
/users.json
, kommer det att visa ett JSON-objekt som innehåller:
[ { "name": "foo", "email": "[email protected]" } ]
Du kan utelämna format.html { render inline: "Hello World" }
om du vill se till att din rutt bara svarar på JSON-förfrågningar.
Styrenheter (grundläggande)
class UsersController < ApplicationController def index respond_to do |format| format.html { render html: "Hello World" } end end end
Detta är en grundkontroll med tillägg av följande rutt (i routes.rb):
resources :users, only: [:index]
Visar Hello World
meddelandet på en webbsida när du går in på URL /users
parametrar
Controllers har tillgång till HTTP-parametrar (du kanske känner dem som ?name=foo
i URL: er, men Ruby on Rails hanterar också olika format!) Och skickar olika svar baserat på dem. Det finns inget sätt att skilja mellan GET- och POST-parametrar, men du bör inte göra det i alla fall.
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
Som vanligt vår rutt:
resources :users, only: [:index]
Åtkomst till URL /users?name=john
och utdatorn kommer att bli Hello John
, åtkomst /users?name=whatever
och utgången blir Hello someone
Filtreringsparametrar (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
Du kan tillåta (eller avvisa) vissa params så att bara vad du vill kommer att passera och du inte kommer att få dåliga överraskningar som alternativ för användarinställningar som inte är avsedda att ändras.
Besöker /users?name=john&sentence=developer
visar Hello john developer
, men besökare /users?name=smith&sentence=spy
visar bara Hello smith
, eftersom :sentence
är bara tillåten när du går in som john
Redirecting
Antar rutten:
resources :users, only: [:index]
Du kan omdirigera till en annan URL med hjälp av:
class UsersController def index redirect_to "http://stackoverflow.com/" end end
Du kan gå tillbaka till föregående sida som användaren besökte med:
redirect_to :back
Observera att i Rails 5 är syntaxen för omdirigering tillbaka annorlunda:
redirect_back fallback_location: "http://stackoverflow.com/"
Vilket kommer att försöka omdirigera till föregående sida och om det inte är möjligt (webbläsaren blockerar HTTP_REFERRER-rubriken) kommer den att omdirigera till :fallback_location
Använda vyer
Antar rutten:
resources :users, only: [:index]
Och styrenheten:
class UsersController < ApplicationController def index respond_to do |format| format.html { render } end end end
app/users/index.html.erb
kommer att visas. Om vyn är:
Hello <strong>World</strong>
Outputen kommer att vara en webbsida med texten: "Hello World "
Om du vill visa en annan vy kan du använda:
render "pages/home"
Och app/views/pages/home.html.erb
kommer att användas istället.
Du kan skicka variabler till vyer med hjälp av inställningsvariabler för controller:
class UsersController < ApplicationController def index @name = "john" respond_to do |format| format.html { render } end end end
Och i app/views/users/index.html.erb
du använda @name
:
Hello <strong><%= @name %></strong>
Och utgången blir: "Hej john "
En viktig anmärkning kring renderingssyntaxen, du kan utelämna render
helt, Rails antar att om du utelämnar det. Så:
class UsersController < ApplicationController def index respond_to do |format| format.html { render } end end end
Kan istället skrivas som:
class UsersController < ApplicationController def index respond_to do |format| format.html end end end
Rails är tillräckligt smart för att räkna ut att det måste göra app/views/users/index.html.erb
.
404 när posten inte hittades
Räddning från posten hittades inte fel istället för att visa ett undantag eller vit sida:
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
Grundläggande REST-styrenhet
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
Visa felsidor för undantag
Om du vill visa för dina användare meningsfulla fel istället för enkla "sorry, något gick fel" har Rails ett trevligt verktyg för ändamålet.
Öppna app/controllers/application_controller.rb
och du bör hitta något liknande:
class ApplicationController < ActionController::Base protect_from_forgery with: :exception end
Vi kan nu lägga till en rescue_from
att återställa från specifika fel:
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
Det rekommenderas att inte rädda från Exception
eller StandardError
annars kan Rails inte visa användbara sidor i händelse av fel.
filter
Filter är metoder som körs "före", "efter" eller "runt" en kontrolleråtgärd. De ärvs, så om du ställer in något i din ApplicationController
kommer de att köras för varje begäran som din ansökan får.
Innan filter
Innan filter körs före kontrollen och kan stoppa begäran (och / eller omdirigera). En vanlig användning är att kontrollera om en användare är inloggad:
class ApplicationController < ActionController::Base
before_action :authenticate_user!
def authenticate_user!
redirect_to some_path unless user_signed_in?
end
end
Innan filter körs på förfrågningar innan begäran kommer till controllerens handling. Det kan returnera ett svar i sig själv och helt förbi handlingen.
Andra vanliga användningar av före-filter validerar användarens autentisering innan de ger dem tillgång till den åtgärd som är avsedd att hantera deras begäran. Jag har också sett dem användas för att ladda en resurs från databasen, kontrollera behörigheter för en resurs eller hantera omdirigeringar under andra omständigheter.
Efter filter
Efter att filter liknar "före", men när de körs efter åtgärdskörningen har de åtkomst till svarobjektet som håller på att skickas. Så kort efter körs filter efter att åtgärden har slutförts. Det kan ändra svaret. Oftast om något görs i ett efterfilter kan det göras i själva åtgärden, men om det finns någon logik att köra efter att ha kört någon av en uppsättning åtgärder, är ett efterfilter ett bra ställe att göra den.
Generellt sett har jag sett efter och runt filter som används för loggning.
Runt filter
Runt filter kan ha logik före och efter åtgärden som körs. Det ger helt enkelt handlingen på vilken plats som helst som behövs. Observera att den inte behöver ge efter för åtgärden och kan köra utan att göra det som ett före-filter.
Runt filter är ansvariga för att utföra deras tillhörande handlingar genom att ge, liknande hur Rack medelvaror fungerar.
Runt om återuppringningar lindrar genomförandet av åtgärder. Du kan skriva ett runt återuppringning i två olika stilar. I den första är återuppringningen en enda bit kod. Den koden kallas innan åtgärden genomförs. Om återuppringningskoden påkallar avkastning utförs åtgärden. När åtgärden är klar fortsätter återuppringningskoden. Således är koden före utbytet som ett återuppringning före åtgärden och koden efter utbytet är återuppringning efter åtgärd. Om återuppringningskoden aldrig åberopar avkastning. åtgärden körs inte - det här är detsamma som att ha en återuppringning innan åtgärden före falsk.
Här är ett exempel på runt-filtret:
around_filter :catch_exceptions
private
def catch_exceptions
begin
yield
rescue Exception => e
logger.debug "Caught exception! #{e.message}"
end
end
Detta kommer att få undantag från alla åtgärder och lägga meddelandet i din logg. Du kan använda runt filter för undantagshantering, inställning och nedrivning, och ett stort antal andra fall.
Endast och utom
Alla filter kan tillämpas på specifika åtgärder med :only
och :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
Hoppa över filter
Alla filter (också ärvda) kan också hoppas över för vissa specifika åtgärder:
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
Eftersom de ärvs, kan filter också definieras i ett namespace
"överordnad" -kontroller. Säg till exempel att du har ett admin
och att du naturligtvis vill att bara adminanvändare ska kunna komma åt det. Du kan göra något liknande:
# 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
Se upp för att du i Rails 4.x kan använda before_filter
tillsammans med before_action
, men before_filter
är för närvarande avskrivet i Rails 5.0.0 och kommer att tas bort i 5.1 .
Genererar en styrenhet
Rails tillhandahåller många generatorer, för naturligtvis också styrenheter.
Du kan generera en ny controller genom att köra det här kommandot i din appmapp
rails generate controller NAME [action action] [options]
Obs: Du kan också använda rails g
alias för att åberopa rails generate
Till exempel för att generera en controller för en Product
, med #index
och #show
åtgärder du skulle köra
rails generate controller products index show
Detta skapar controller i app/controllers/products_controller.rb
, med båda de åtgärder du angav
class ProductsController < ApplicationController
def index
end
def show
end
end
Det kommer också att skapa en products
mapp inuti app/views/
innehåller de två mallar för handkontrollen handlingar (dvs. index.html.erb
och show.html.erb
, notera att förlängningen kan variera beroende på din mall motor, så om du använder slim
, till exempel kommer generator att skapa index.html.slim
och show.html.slim
)
Om du angav några åtgärder kommer de också att läggas till i din routes
# config/routes.rb
get 'products/show'
get 'products/index'
Rails skapar en hjälparfil för dig, i app/helpers/products_helper.rb
, och även tillgångsfilerna i app/assets/javascripts/products.js
och app/assets/stylesheets/products.css
. När det gäller vyer ändrar generatorn detta beteende beroende på vad som anges i din Gemfile
: dvs. om du använder Coffeescript
och Sass
i din applikation kommer generatorgeneratorn istället att generera products.coffee
och products.sass
.
Till sist, men inte minst, genererar Rails också testfiler för din controller, din hjälpare och dina åsikter.
Om du inte vill att något av dessa ska skapas, kan du säga Rails att hoppa över dem, beroende på vilket alternativ du vill
--no-
eller - --skip
, så här:
rails generate controller products index show --no-assets --no-helper
Och generatorn kommer att hoppa över både assets
och helper
Om du behöver skapa en kontroller för ett specifikt namespace
lägger du till det framför NAME
:
rails generate controller admin/products
Detta skapar din controller inuti app/controllers/admin/products_controller.rb
Rails kan också generera en komplett RESTful-controller för dig:
rails generate scaffold_controller MODEL_NAME # available from Rails 4
rails generate scaffold_controller Product
Rädda ActiveRecord :: RecordNotFound med omdirigering_till
Du kan rädda ett RecordNotFound-undantag med en omdirigering istället för att visa en felsida:
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