在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Ruby/ 第五章 Rails 中的控制器
寫在后面
寫在前面
第六章 Rails 的配置及部署
第四章 Rails 中的模型
4.4 模型中的校驗(yàn)(Validates)
1.3 用戶界面(UI)設(shè)計
6.5 生產(chǎn)環(huán)境部署
3.2 表單
4.3 模型中的關(guān)聯(lián)關(guān)系(Relations)
4.5 模型中的回調(diào)(Callback)
第五章 Rails 中的控制器
4.2 深入模型查詢
5.2 控制器中的方法
6.2 緩存
3.4 模板引擎的使用
6.4 I18n
第一章 Ruby on Rails 概述
6.6 常用 Gem
1.2 Rails 文件簡介
2.2 REST 架構(gòu)
2.3 深入路由(routes)
第三章 Rails 中的視圖
6.3 異步任務(wù)及郵件發(fā)送
第二章 Rails 中的資源
3.3 視圖中的 AJAX 交互

第五章 Rails 中的控制器

課程概要:

本課程通過對控制器的學(xué)習(xí),了解 Rails 如何通過處理請求和作出相應(yīng)來控制邏輯的,并且完成網(wǎng)店中購物和支付流程。

知識點(diǎn):

  1. 控制器中的請求和相應(yīng)
  2. 控制器中的方法

課程背景

控制器 Controller 是 MVC 中調(diào)度員的角色,它接收客戶端發(fā)送過來的請求,并且通過我們編寫的代碼作出相應(yīng),實(shí)現(xiàn)業(yè)務(wù)邏輯的控制。

5.1 控制器中的請求和相應(yīng)

概要

本課時講解控制器中如何處理傳入的參數(shù)和相應(yīng),并且介紹在請求和相應(yīng)的過程中,如何處理請求參數(shù),使用 sesson,設(shè)置 etag 緩存和使用 csrf 確保數(shù)據(jù)來源安全。

知識點(diǎn)

  • request
  • response
  • params
  • respond_to
  • session
  • cookies
  • etag
  • csrf

正文

5.1.1 Action Pack

Action Pack 是 Rails 種又一個核心的 Gem,它可以處理 web 請求,使用 routes 中定義的規(guī)則調(diào)用控制器(Controller)及方法(Action),并且自動判斷請求類型,做出對應(yīng)的相應(yīng)。

Rails 中的控制器,指的就是處理請求及做出相應(yīng)。

5.1.2 Request 類

ActionDispatch::Request 類是對 web 請求的包裝類,它有兩個常用的方法:

request.headers["Content-Type"] # => "text/plain"

headers 包含了請求的頭信息。

request.parameters

它會返回請求的參數(shù),不過我們并不直接使用它,而是使用 params 方法獲得,這在稍后介紹。

Request 類的源代碼在這里

5.1.3 Response 類

ActionDispatch::Response 類代表了響應(yīng)結(jié)果,它也有常用的方法,不過我們更經(jīng)常用的是 Controller 中的 action 和回調(diào)。在一些測試代碼中,我們經(jīng)常使用 response 實(shí)例。

比如,我們測試商品刪除之后,會返回到商品列表,我們的測試代碼是:

RSpec.describe ProductsController, type: :controller do
  ...
  describe "DELETE #destroy" do
    it "redirects to the products list" do
      product = Product.create! valid_attributes
      delete :destroy, {:id => product.to_param}, valid_session
      expect(response).to redirect_to(products_url)
    end
  end
end

Request 和 Response 在我們的業(yè)務(wù)邏輯代碼中并不不常用到,下面介紹的內(nèi)容,是我們在編寫控制器代碼時,經(jīng)常遇到的。

5.1.4 strong paramaters

Controller 是控制器的概念,所謂控制,指在網(wǎng)絡(luò)傳輸中,接收參數(shù)和做出相應(yīng)。Controller 有兩種方式接收參數(shù):GET 和 POST。兩種方式均可通過 params 讀取傳遞的內(nèi)容。

在 Rails3之前的版本中,當(dāng)接收傳遞的參數(shù),用來更新資源屬性時,可以設(shè)定 Model 的屬性白名單,非報名單上的屬性不允許通過參數(shù)傳遞的方式修改,比如:

class User < ActiveRecord::Base
  attr_accessible :name
end

在 Rails 4 之后,這個方法轉(zhuǎn)為 gem,不再是 Rails 4 的核心功能,但將在 Rails 5 中重新回到核心功能中?,F(xiàn)在,使用 permit 方法來過濾參數(shù)。使用 scaffold 創(chuàng)建的 Controller 默認(rèn)使用了該方法:

class ProductsController < ApplicationController
  def create
    @product = Product.new(product_params)
    ...
  private
    def product_params
      params.require(:product).permit(:name, :price, :description)
    end
end

permit 可以設(shè)定關(guān)聯(lián)關(guān)系的屬性:

params.require(:product).permit(:name, :price, :description, variants_attributes: [:price, :size, :id, :_destroy])

:id:_destroy 適用于上一章介紹的 accepts_nested_attributes_for 方法。

5.1.5 respond_to 方法

Controller 響應(yīng)請求有多種結(jié)果,響應(yīng)返回 Status Code,常見的有 200(成功響應(yīng)),302(跳轉(zhuǎn)),404(未找到資源),500(內(nèi)部錯誤)。更多響應(yīng) Code 參考 3.3 視圖中的 AJAX 交互

一個 controller 的 action 對應(yīng)一個請求,這樣可以保持我們業(yè)務(wù)邏輯代碼清晰,易維護(hù)。一個 action 可以響應(yīng)一個請求的多中類型,這在我們第三章里已經(jīng)有了介紹和演示。

Controller 使用 respond_to 方法,針對每一種請求類型,做出響應(yīng):

respond_to do |format|
  if @product.save
    format.html { redirect_to @product, notice: 'Product was successfully created.' }
    format.json { render :show, status: :created, location: @product }
  else
    format.html { render :new }
    format.json { render json: @product.errors, status: :unprocessable_entity }
  end
  format.js
end

當(dāng)我們處理多個資源時,每個資源的 createupdate 等資源方法,大多都具備相同的邏輯代碼。除了特定的業(yè)務(wù)邏輯,他們都會響應(yīng)典型的資源操作。 Rails 4.2 之前提供了 respond_with 訪問,4.2 之后將它轉(zhuǎn)為一個 gem,我們安裝這個 gem:

gem "responders"

并且創(chuàng)建文件:

% rails g responders:install
      create  lib/application_responder.rb
      insert  config/application.rb
     prepend  app/controllers/application_controller.rb
      insert  app/controllers/application_controller.rb
      create  config/locales/responders.en.yml

默認(rèn),它只支持 :html,因?yàn)槲覀冄菔緯r,又使用到了 :json 和 :js,還有 :xml,我們將這些類型添加上:

class ApplicationController < ActionController::Base
  self.responder = ApplicationResponder
  respond_to :html, :xml, :json, :js

我們將剛才 respond_to 方法改成 respond_with,精簡重復(fù)的代碼(Dry up your code):

def create
  @product = Product.create(product_params)
  respond_with(@product)
end

6.4 I18n 中,我們講 I18n 文件做了整理,這里我們把 generator 創(chuàng)建的語言包,按照 6.4 一節(jié)中介紹的方式進(jìn)行管理,并且增加中文提示。如此,我們不必為每個資源創(chuàng)建、修改等操作各自編寫語言提示了。

5.1.6 session 和 cookies

從一個請求到另一個請求,Rails 使用 Session 來保存一些簡單的信息,比如 user_id 等。同時,也可以用 cookies 保存該信息。

當(dāng) Rails 項(xiàng)目創(chuàng)建的時候,它會有一個默認(rèn)的 cookie name,這在 config/initializers/session_store.rb 中:

Rails.application.config.session_store :cookie_store, key: '_rails-practice_session'

這里,我們用 cookie_store 來儲存 session,當(dāng)我們在項(xiàng)目中保存 session 的時候,數(shù)據(jù)會保存在這個 cookie 中。

http://wiki.jikexueyuan.com/project/rails-practice/images/chapter_5/1.png" alt="" />

在 Rails 2 之前,可以 decode 這個內(nèi)容,查看其中 session 的內(nèi)容:

require 'rack'
cookie = "WmQyNFliZnprd3..."
Rack::Session::Cookie::Base64::Marshal.new.decode(cookie)
=> {"session_id"=>"d3b17...", "user_id"=>"123", "_csrf_token"=>"rtkofT..."}

因?yàn)樵?Rails 3 中已經(jīng)增加了 secret_key_base,所以無法直接 decode 內(nèi)容了。

但是,如果單獨(dú)使用一個 cookie 來記錄數(shù)據(jù),默認(rèn)是不經(jīng)過任何加密的:

cookies[:name] = "Rails"

http://wiki.jikexueyuan.com/project/rails-practice/images/chapter_5/2.png" alt="" />

如果這個數(shù)據(jù)不想被暴露,需要單獨(dú)加密:

cookies.signed[:name] = "Rails"
cookies.permanent.signed[:name] = "Rails" [1]

permanent 會讓這個 cookie 有20年的有效時間。

Cookie 的 api 文檔在這里。

如果我們在 Cookie 中保存了過多數(shù)據(jù),會超出 cookie 的大小限制,這時我們可以更改 session 的保存方式,比如使用 redis,memcached 等。

Rails.application.config.session_store :redis_store, servers: {
  host: "127.0.0.1",
  port: 6379,
  namespace: "store_session"}

6.2 緩存 中有其他詳細(xì)的介紹。

5.1.7 etag

Controller 響應(yīng)的時候,header 中會包含 etag 屬性,根據(jù)這個屬性,瀏覽器會判斷該內(nèi)容是否修改。

headers['ETag'] = Digest::MD5.hexdigest(body)

但對 Rails 的布局和模板而言,經(jīng)常包含變動的內(nèi)容,比如登錄后會顯示用戶名稱,未登錄顯示登錄連接。 并且,body 可能會很大,md5 時間長。

我們可以針對資源,單獨(dú)增加 etag:

def show
  fresh_when([@product, current_user.try(:id)])
end

也可以將它精簡:

class ProductsController < ApplicationController
  etag { current_user.try(:id) }
  ...
  def show
    fresh_when(@product)
  end

如果我們僅提供數(shù)據(jù),比如 api,可以去掉模板

fresh_when @product, template: false

5.1.8 csrf

在 Controller 接收請求數(shù)據(jù)的時候,安全機(jī)制會處理跨站請求偽造(cross-site request forgery,簡稱 CSRF)。在我們的布局(layout)頁面,你可能已經(jīng)看到這樣一個輔助方法:

<%= csrf_meta_tags %>

打開頁面的源碼,我們可以看到:

<meta name="csrf-param" content="authenticity_token" />
<meta name="csrf-token" content="O3Li25wJK0buXKRQRX4CzpAWheQIQ4VknCPe3KwNIFkIuUsbBApxl2jVVTd9IcmzR8oHLZI0qZpO39aLdNaBAQ==" />

當(dāng)我使用表單的輔助方法 form_forform_tag 時,表單會自動創(chuàng)建一個隱藏控件

<input type="hidden" name="authenticity_token" value="GI5YwKDhQA4pMlLRaUlpHugYdL5ygNe3Co6TL8PvZDsrRfEAOOIa36+7o7ZRFqJjP8T2d+j3+0nYcpt4GzTFYw==">

當(dāng)我們使用 remote: true 時,這個控件又消失了,這樣是不是不安全?不,ujs 在提交的時候,為我們自動補(bǔ)充上了 authenticity_token 參數(shù)。

更多 Rails 安全問題,可以參考這里 http://guides.ruby-china.org/security.html

注:

感謝 Rails 4 - Zombie Outlaws,本節(jié) 3,5 的內(nèi)容靈感來自。