Action Controller Overview, Season 2






Total Views
Views on SlideShare
Embed Views



14 Embeds 1,266 779
http://localhost 208 104 92 34 19 12 8 3 3 HTTP 1 HTTP 1 HTTP 1 1



Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
Post Comment
Edit your comment

Action Controller Overview, Season 2 Action Controller Overview, Season 2 Presentation Transcript

  • Ror lab. season 2 - the 15th round -Action Controller Overview February 2th, 2013 Hyoseong Choi
  • A Controller• RESTful applications• like an orchestra conductor• as a middle man btw models and views
  • REST URI with HTTP methods#index #new #edit GET /books HTTP/1.1 GET /books/new HTTP/1.1 GET /books/1/edit HTTP/1.1 Host: Host: Host: Accept: application/xml Accept: application/xml Accept: application/xml#show #create #update GET /books/1 HTTP/1.1 POST /books HTTP/1.1 PUT /books/1 HTTP/1.1 Host: Host: Host: Accept: application/xml Accept: application/xml Accept: application/xml #destroy DELETE /books/1 HTTP/1.1 Host: Accept: application/xml
  • URI? http://domain_name/posts/114• Uniform Resource Identifier = URL + URN = Uniform Resource Locator + Uniform Resource Name
  • Routing$ rake routes CONTROLLER=books books GET /books(.:format) books#index POST /books(.:format) books#create new_book GET /books/new(.:format) books#newedit_book GET /books/:id/edit(.:format) books#edit book GET /books/:id(.:format) books#show PUT /books/:id(.:format) books#update DELETE /books/:id(.:format) books#destroy named HTTP resource Controllers routes verbs URI Actions
  • MVC
  • MVC Controller View Model
  • Methods & Actions• A “class” has methods• A controller < ApplicationController• public methods => “action” <= routing actions $ rake routes CONTROLLER=clients clients GET /clients(.:format) clients#index POST /clients(.:format) clients#create new_client GET /clients/new(.:format) clients#new edit_client GET /clients/:id/edit(.:format) clients#edit client GET /clients/:id(.:format) clients#show PUT /clients/:id(.:format) clients#update DELETE /clients/:id(.:format) clients#destroy
  • Parameters• Two kinds of parameters - Query string - POST data• by params hash for both of these• “Routing” parameters
  • Hash & Array Params for Query String GET /clients?ids[]=1&ids[]=2&ids[]=3 params[:ids] ? [ “1”, “2”, “3”]
  • Hash & Array Params for POST data<form accept-charset="UTF-8" action="/clients" method="post">  <input type="text" name="client[name]" value="Acme" />  <input type="text" name="client[phone]" value="12345" />  <input type="text" name="client[address][postcode]" value="12345" />  <input type="text" name="client[address][city]" value="Carrot City" /></form> { “name” => “Acme”, “phone” => “12345”,params[:client] ? “address” => { “postcode” => “12345”, “city” => “Carrot City” } }
  • JSON/XML ParamsJSON parameter{ "company": { "name": "acme", "address": "123 Carrot Street" } } automatically converted to params hash by Railsparams[:company]{ :name => “acme”, “address” => “123 Carrot Street” }
  • default_url_options➙ global default parameter class PostsController < ApplicationController   # The options parameter is the hash passed in to url_for   def default_url_options(options)     {:locale => I18n.locale}   end end
  • url_for<%= url_for(:action => index) %># => /blog/<%= url_for(:action => find, :controller => books) %># => /books/find<%= url_for(:action => login, :controller => members, :only_path => false, :protocol => https) %># =><%= url_for(:action => play, :anchor => player) %># => /messages/play/#player<%= url_for(:action => jump, :anchor => tax&ship) %># => /testing/jump/#tax&ship<%= url_for( %># relies on Workshop answering a persisted? call (and in this case returning false)# => /workshops<%= url_for(@workshop) %># calls @workshop.to_param which by default returns the id# => /workshops/5# to_param can be re-defined in a model to provide different URL names:# => /workshops/1-workshop-name<%= url_for("") %># =><%= url_for(:back) %># if request.env["HTTP_REFERER"] is set to ""# =><%= url_for(:back) %># if request.env["HTTP_REFERER"] is not set or is blank# => javascript:history.back()
  • Session stateless ➤ stateful in controllersSession Repositories :• ActionDispatch::Session::CookieStore• ActionDispatch::Session::CacheStore• ActionDispatch::Session:: ActiveRecordStore• ActionDispatch::Session::MemCacheStore
  • Session Accessing the Session lazily loadedclass LoginsController < ApplicationController  # "Create" a login, aka "log the user in"  def create    if user = User.authenticate(params[:username], params[:password])      # Save the user ID in the session so it can be used in      # subsequent requests      session[:current_user_id] =      redirect_to root_url    end  endend
  • Session Removing a Session Keyclass LoginsController < ApplicationController  # "Delete" a login, aka "log the user out"  def destroy    # Remove the user id from the session    @_current_user = session[:current_user_id] = nil    redirect_to root_url  endendcf. reset_session
  • Session Flash a disposable session• special session : Hash• cleared with each request• only available in the next request• useful for storing error messages etc
  • Session Flashredirect_to root_url, notice: "You have successfully logged out."redirect_to root_url, alert: "Youre stuck here!"redirect_to root_url, flash: { referral_code: 1234 }
  • Session Flash<html>  <!-- <head/> -->  <body>    <% flash.each do |name, msg| -%>      <%= content_tag :div, msg, class: name %>    <% end -%>     <!-- more content -->  </body></html>
  • Session Flash<% if flash[:just_signed_up] %>  <p class="welcome">Welcome to our site!</p><% end %>
  • Session Flash flash.keepclass MainController < ApplicationController  # Lets say this action corresponds to root_url, but you want  # all requests here to be redirected to UsersController#index.  # If an action sets the flash and redirects here, the values  # would normally be lost when another redirect happens, but you  # can use keep to make it persist for another request.  def index    # Will persist all flash values.    flash.keep     # You can also use a key to keep only some kind of value.    # flash.keep(:notice)    redirect_to users_url  endend
  • Session Flash flash.nowclass ClientsController < ApplicationController  def create    @client =[:client])    if      # ...    else[:error] = "Could not save client"      render :action => "new"    end  endend
  • Cookies• persisted across request and even sessions class CommentsController < ApplicationController   def new     # Auto-fill the commenters name if it has been stored in a cookie     @comment = => cookies[:commenter_name])   end     def create     @comment =[:comment])     if       flash[:notice] = "Thanks for your comment!"       if params[:remember_name]         # Remember the commenters name.         cookies[:commenter_name] =       else         # Delete cookie for the commenters name cookie, if any.         cookies.delete(:commenter_name)       end       redirect_to @comment.article     else       render :action => "new"     end   end end
  • Cookies• to delete a cookie value cookies.delete(:key)
  • xml & json dataclass UsersController < ApplicationController  def index    @users = User.all    respond_to do |format|      format.html # index.html.erb      format.xml  { render :xml => @users}      format.json { render :json => @users}    end  endend
  • Filters methods inherited before Action afterto halt action not to halt action around
  • Filtersclass ApplicationController < ActionController::Base  before_filter :require_login   private   def require_login    unless logged_in?      flash[:error] = "You must be logged in to access this section"      redirect_to new_login_url # halts request cycle    end  end   # The logged_in? method simply returns true if the user is logged  # in and false otherwise. It does this by "booleanizing" the  # current_user method we created previously using a double ! operator.  # Note that this is not common in Ruby and is discouraged unless you  # really mean to convert something into true or false.  def logged_in?    !!current_user  endend booleanizing operator
  • Filters• skip_before_filter class LoginsController < ApplicationController   skip_before_filter :require_login, :only => [:new, :create] end
  • Filters• around_filter class ChangesController < ActionController::Base   around_filter :wrap_in_transaction, :only => :show     private     def wrap_in_transaction     ActiveRecord::Base.transaction do       begin         yield       ensure         raise ActiveRecord::Rollback       end     end   end end
  • Filters• Three Ways to use Filters - a private method - a block - a class
  • Filters• Using a Block in more simple cases class ApplicationController < ActionController::Base   before_filter do |controller|     redirect_to new_login_url unless controller.send(:logged_in?)   end end
  • Filters• Using a Class in more complex cases class ApplicationController < ActionController::Base   before_filter LoginFilter end   class LoginFilter   def self.filter(controller)     unless controller.send(:logged_in?)       controller.flash[:error] = "You must be logged in"       controller.redirect_to controller.new_login_url     end   end end
  • CSRF• Site to site hacking• First step for this with non-GET request :create/update/destroy• RESTful default to protect CSRF• Nevertheless, non-GET request still vulnerable
  • CSRF• To add a non-guessable token with form helpers <%= form_for @user do |f| %>   <%= f.text_field :username %>   <%= f.text_field :password %> <% end %> <form accept-charset="UTF-8" action="/users/1" method="post"> <input type="hidden"        value="67250ab105eb5ad10851c00a5621854a23af5489"        name="authenticity_token"/> <!-- fields --> </form>
  • CSRF• form_authenticity_token in custom Ajax calls
  • Request & Response Objects• Two methods in every controller - `request` method => request object - `response` method => response object
  • Request Objects
  • Request Objects• Three hash parameters for request objects - path_parameters - query_parameters : query string - request_parameters : post data
  • Response Objects• like in an `after` filter
  • Response Objects response.headers["Content-Type"] = "application/pdf"
  • HTTPAuthentications
  • HTTP Authentications• Two types - Basic authentication : using base 64 encoding - Digest authentication : using MD5 encoding
  • HTTP Authentications• Basic authentication class AdminController < ApplicationController   http_basic_authenticate_with :name => "humbaba", :password => "5baa61e4" end
  • HTTP Authentications• Digest authentication class AdminController < ApplicationController   USERS = { "lifo" => "world" }     before_filter :authenticate     private     def authenticate     authenticate_or_request_with_http_digest do |username|       USERS[username]     end   end end Safari v 6.0.2 undefined method `unpack for nil:NilClass
  • Streaming & File Downloads•send_data•send_file require "prawn" class ClientsController < ApplicationController   # Generates a PDF document with information on the client and   # returns it. The user will get the PDF as a file download.   def download_pdf     client = Client.find(params[:id])     send_data generate_pdf(client),               :filename => "#{}.pdf",               :type => "application/pdf", :disposition => "attachement"   end     private     def generate_pdf(client) do       text, :align => :center       text "Address: #{client.address}"       text "Email: #{}"     end.render   end end send_data
  • Streaming & File Downloadsclass ClientsController < ApplicationController  # Stream a file that has already been generated and stored on disk.  def download_pdf    client = Client.find(params[:id])    send_file("#{Rails.root}/files/clients/#{}.pdf",              :filename => "#{}.pdf",              :type => "application/pdf")  endend send_file
  • Streaming & File Downloadsin a RESTful application #153(revised) require "prawn" class ClientsController < ApplicationController     def show     @client = Client.find(params[:id])       respond_to do |format|       format.html       format.pdf { render :pdf => generate_pdf(@client) }     end   end   private     def generate_pdf(client) do       text, :align => :center       text "Address: #{client.address}"       text "Email: #{}"     end.render   end end send_file
  • Streaming & File Downloads • in config/initializer/mime_types.rbin a RESTful application Mime::Type.register "application/pdf", :pdf send_file
  • Parameter Filtering• in config/application.rbconfig.filter_parameters << :password
  • Rescueclass ApplicationController < ActionController::Base  rescue_from User::NotAuthorized, :with => :user_not_authorized   private   def user_not_authorized    flash[:error] = "You dont have access to this section."    redirect_to :back  endend class ClientsController < ApplicationController  # Check that the user has the right authorization to access clients.  before_filter :check_authorization   # Note how the actions dont have to worry about all the auth stuff.  def edit    @client = Client.find(params[:id])  end   private   # If the user is not authorized, just throw the exception.  def check_authorization    raise User::NotAuthorized unless current_user.admin?  endend
  • Force HTTPS protocolclass DinnerController  force_sslendclass DinnerController  force_ssl :only => :cheeseburger  # or  force_ssl :except => :cheeseburgerend # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. config.force_ssl = true
  • 감사합니다.