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ållet Hello 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


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow