iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 15
0
Modern Web

Ruby礦工的Rails地圖系列 第 15

淺談validation -- 資料的守門人

猶豫到最後一小時,真想不到其他主題
最後決定來寫資料驗證(validation)
之所以不想寫這個主題,是因為我在這邊沒什麼特別的心得
寫起來就會很像教科書抄錄
跟文件或API指引沒有太大的不同
頂多只是用我自己的邏輯消化一遍

無論如何,循著model裡的元素一個一個介紹
這樣的模式令人很放心,就決定依序進行下去
我這幾天陸續談了「淺談scope -- 常用的條件通通藏在裡面」
「淺談delegate -- 探囊取物」
加上今天這篇,好像默默自成一種風格


資料驗證是網站很重要的一部分
做得不好不只可能造成錯誤,更嚴重可能有資安上的隱憂
如果由使用者到資料庫、由前往後看
其實有很多階段都可以做驗證
有一個觀念很重要:

「讓錯誤的資料越早被擋下來越好」

也就是能在前端擋就不要在後端擋;
能在後端擋就不要用資料庫擋。
不過rails很貼心的做了前後端整合
只要有做驗證,前端會自動幫忙擋下
最基本的驗證如下:

class User < ActiveRecord::Base
    validates_presence_of :name
end

很明顯是在User.name判斷不可為空
這樣在前端新增或編輯時,就會自動檢查並且擋下
也可以使用這種寫法,似乎是新的建議寫法:

class User < ActiveRecord::Base
    validates :name, presence: true
end

如果要同時驗證多個欄位,只需要逗號分隔
除了是否為空之外,也可以驗證許多不同的資料細節,例如

class User < ActiveRecord::Base
    validates :name, :email, presence: true
    #同時驗證多個欄位
    validates :price, presence: true, numericality: { only_integer: true }
    #只能是數字
    validates :age, length: {minimum: 5, maximum: 100}
    #長度最大100,最小5
    validates :age, length: { in: 5..100 }
    #或是這樣寫
    validates :email, uniqueness: true
    #必須唯一,當然這也可以在資料庫階段就防止
end

還有很多很多,這邊就不列舉
如果原生的方法都不滿意,也可以寫自己客製化的validate
我這邊直接提供官方的範例如下:(因為我沒有自己實作過XD)

class GoodnessValidator < ActiveModel::Validator
  def validate(record)
    if record.first_name == "Evil"
      record.errors[:base] << "This person is evil"
    end
  end
end
 
class Person < ApplicationRecord
  validates_with GoodnessValidator
end

只想在特定的動作才驗證,不想要隨時驗證,也可以

validates :email, uniqueness: true, on: :create
#只有在create的時候才驗證唯一

或是條件相當複雜,可以獨立為一個方法

class Order < ApplicationRecord
  validates :card_number, presence: true, if: :paid_with_card?
  #只有當訂單為信用卡時,信用卡號碼才不可為空
  def paid_with_card?
    payment_type == "card"
  end
end

設置上相當靈活
但是凡事總有例外,如果千辛萬苦設定了一堆驗證
假如有資料要偷跑,略過驗證怎麼辦?
別擔心,只要如下:

event.save( validate: false )

剛剛的條件都可以不算數啦!!
是不是很方便呢?

資料驗證相當重要,而且要記得處理好驗證不過的處理唷
像我就吃過相當多資料驗證不過導致沒存進資料庫
但是程式照樣執行的悶虧,不可不慎呢


上一篇
淺談delegate -- 探囊取物
下一篇
Active Record 查詢
系列文
Ruby礦工的Rails地圖30

尚未有邦友留言

立即登入留言