[Rails]如何使用Rails中的params

in #cn6 years ago (edited)

什麼是params

params是一個包含用戶所有傳進來的參數Hash(事實上params是來自於ActiveSupport::HashWithIndifferentAccess的object)。

一個params,至少一定會包含:controller和:action兩個key。我們可以直接透過params知道這個request是對應哪個controller跟action

# 對pages/index送出請求後得到的params
=> <ActionController::Parameters {"controller"=>"pages", "action"=>"index"} permitted: false>

兩種params來源

Rails透過兩種方式來透過params傳遞訊息,Query String參數或 POST參數。而在Rails並不區分,兩者都可以在controller中被取用。我們待會會針對兩種方式分別介紹。

Query String Parameters

第一種是透過url,又稱做作query string parameters。Query String 是url中?號後面的任何字串,通常是透過HTTP GET傳遞。

舉例來說,當我們對以下的url送出請求

localhost:3000/pages?state=cool

我們可以得到以下的params

params
=> <ActionController::Parameters {"state"=>"cool", "controller"=>"pages", "action"=>"index"} permitted: false>

使用form表單送出get request

假設你使用get的form表單,你也可以透過query string parameters來傳遞訊息到params中。

<%= form_tag "/", method: :get do %>
  <%= label_tag :name %>
  <%= text_field_tag :name %>
  <%= label_tag :age %>
  <%= text_field_tag :age %>
  <%= submit_tag "Submit" %>
<% end %>

接著在controller中下byebug

class PagesController < ApplicationController
  def index
    byebug
  end
end

頁面看起來像這樣

送出請求後在byebug停下來的地方下params指令會得到

<ActionController::Parameters {"utf8"=>"✓", "name"=>"Franklin Graham", "age"=>"43", "commit"=>"Submit", "controller"=>"homes", "action"=>"show"} permitted: false>

讓指令繼續跑完,照理說你的url會變成這樣

所以即使你是透過form表單送出get request,最後也是變成query string parameters的方式傳遞訊息。

HTTP Post

另外一種傳遞params的方式,是透過Rails的HTTP Post方法。

讓我們建立一個表單和對應的action。

<%= form_for @page do |f| %>
  <%= f.label :user %>
  <%= f.text_field :user %>
  <%= f.submit "送出" %>
<% end %>
# controller/pages_controller.rb
Class PagesController < ApplicationController
  def create
    @page = Page.new(page_params)
    @page.save
  end
end

而我們在controller可以得到params如下

params
=> <ActionController::Parameters {"utf8"=>"✓", "authenticity_token"=>"nZgtu+hFRmIbLcZU+uVcfRLdXXYW0J4U005GzeoCFu2eY9kW7Nu2niJu9Bf+aCvCxlsc2QclvCQGzBYU5z2+xQ==", "page"=>{"user"=>"Elon Musk"}, "commit"=>"送出", "controller"=>"pages", "action"=>"create"} permitted: false>

如果你仔細觀察的話你會發現,透過query string parameter跟Post request傳遞的params基本上沒有太大的差異,這也是為什麼我們說Rails並不區分兩者的原因。

Hash and Array Parameters

如果想要在params中產生nested array的話,你可以這樣做

https://example.com/clients?ids[]=1&ids[]=2&ids[]=3

這樣的話,透過params我們就可以得到

params[:ids]
=> ["1", "2", "3"]
<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>

When this form is submitted, the value of params[:client] will be { "name" => "Acme", "phone" => "12345", "address" => { "postcode" => "12345", "city" => "Carrot City" } }. Note the nested hash in params[:client][:address].

路徑路由(Routing Parameters)

前面提到了兩種不同傳遞params的方式,這邊我們介紹透過路由的設定,達到預先設定params的key和value的方法。

另外,還會講解path

預先設定key

先讓我們設定以下的路徑

get '/pages/:status' => 'pages#index'

當使用者送出一個get request到/clients/active時,params[:state]的value就會被設定成active。

所以我們可以透過params得到

params[:state]
=> "active"

預先設定key、value

依舊是跟剛剛相同的路由,但我們而外加入一個hash{foo: bar}

get '/pages/:status' => 'pages#index', foo: 'bar'

這樣params[:foo]就會被設定成"bar"

params[:foo]
=> "bar"

傳遞參數到url helper中

寫過一段時間Rails的人,一定對這樣的寫法感到不陌生

link_to "Edit Page", edit_page_path(@page)

當我們點擊了Edit Page之後,我們基本上會被帶到以下的網址。

localhost:3000/pages/3/edit

我們可以仔細觀查這個路由的uri pattern

/pages/:id/edit(.:format)

這表示當我們把object塞到path裡時,這個edit_page_path會透過塞進來的object,得到該object的id,並產生對應的路由。也就是說edit_page_path(@page)會變成

edit_page_path(@page)
=> localhost:3000/pages/3/edit

雖然這個效果很方便,但這神奇的效果是怎麼做到的?

這個效果是因為在產生路徑的過程當中,Rails會觸發一個to_params的方法,將object轉換成該object的id。

def to_param
  id && id.to_s
end

此外,我們也可以另外path設定額外的params key和value。

edit_page_path(@page, state: "active")
=> localhost:3000/pages/3/edit?state=active

參考資料

Action Controller 概覽
路由(Routing)
Friendly URLs

Coin Marketplace

STEEM 0.18
TRX 0.15
JST 0.029
BTC 62907.73
ETH 2531.30
USDT 1.00
SBD 2.62