Sails 捆綁支援自動驗證您模型的屬性。每當記錄被更新或建立新記錄時,每個屬性的資料都會根據您預先定義的所有驗證規則進行檢查。這提供了一個方便的安全機制,以確保無效的條目不會進入您應用程式的資料庫。
除了 unique
(以資料庫層級約束實作;請參閱「Unique」),以下所有驗證都在 JavaScript 中實作,並在與 Sails 相同的 Node.js 伺服器程序中執行。另外請記住,無論使用何種驗證,屬性都必須始終指定其中一種內建資料類型(string
、number
、json
等)。
// User
module.exports = {
attributes: {
emailAddress: {
type: 'string',
unique: true,
required: true
}
}
};
在 Sails/Waterline 中,模型屬性始終具有某種資料類型保證。這超越了底層資料庫中可能存在的任何實體層約束—它更像是為開發人員提供一種方法,以維持對特定模型輸入或輸出的資料的合理假設。
此資料類型保證用於結果和條件的邏輯驗證和強制轉換。以下是 Sails 和 Waterline 支援的資料類型列表
資料類型 | 用法 | 描述 |
---|---|---|
type: 'string' |
任何字串。 | |
type: 'number' |
任何數字。 | |
type: 'boolean' |
true 或 false 。 |
|
type: 'json' |
任何可序列化為 JSON 的值,包括數字、布林值、字串、陣列、字典(純 JavaScript 物件)和 null 。 |
|
type: 'ref' |
除了 undefined 之外的任何 JavaScript 值。(應僅在利用介面卡特定的行為時使用。) |
Sails 的 ORM (Waterline) 及其介面卡執行寬鬆驗證,以確保條件字典中提供的值以及作為 .create()
或 .update()
的值與預期的資料類型相符。
注意: 在不原生支援JSON.stringify()
,然後儲存在類型設定為 text
的欄位中。每次傳回記錄時,資料都會呼叫 JSON.parse()
。在考慮效能以及與其他應用程式或資料庫中現有資料的相容性時,需要注意這一點。官方的 PostgreSQL 和 mongoDB 介面卡可以原生讀取和寫入
string
、number
和 boolean
資料類型在建立或更新記錄時,不接受 null
作為值。為了允許設定 null
值,請切換屬性上的 allowNull
旗標。allowNull
旗標僅對上述資料類型有效;它對類型為 json
或 ref
的屬性、任何關聯或任何主鍵屬性無效。
由於空字串 ("") 是一個字串,因此通常受 type: 'string'
屬性支援;但有幾個例外:主鍵(因為主鍵永遠不支援空字串)和任何具有 required: true
的屬性。
如果屬性為 required: true
,則在呼叫 .create()
時,必須始終為其指定一個值。這也防止在建立或更新時將值設定為 null
或空字串 ("")。
以下所有驗證規則都不會對 null
施加任何額外限制。也就是說,如果通常允許 null
,則啟用 isEmail
驗證規則不會導致 null
被拒絕為無效。
同樣地,以下大多數驗證規則都不會對空字串 ("") 施加任何額外限制。有一些例外(isNotEmptyString
和非字串相關的規則,例如 isBoolean
、isNumber
、max
和 min
),但除此之外,對於通常允許空字串 ("") 的任何屬性,新增驗證規則不會導致它被拒絕。
在下表中,「相容屬性類型」欄顯示了每個驗證規則適用的資料類型(即屬性定義的 type
屬性)。在許多情況下,一個驗證規則可以與多種類型一起使用。請注意,下表採用了一個捷徑:如果與
規則名稱 | 檢查內容 | 使用注意事項 | 相容屬性類型 |
---|---|---|---|
custom(自訂) | 一個值,當作為自訂函數的第一個參數提供時,該函數傳回 true 。 |
範例 | 任何 |
isAfter | 一個值,當解析為日期時,指的是晚於配置的 JavaScript Date 實例的時刻。 |
isAfter: new Date('Sat Nov 05 1605 00:00:00 GMT-0000') |
|
isBefore | 一個值,當解析為日期時,指的是早於配置的 JavaScript Date 實例的時刻。 |
isBefore: new Date('Sat Nov 05 1605 00:00:00 GMT-0000') |
|
isBoolean | 一個值,是 true 或 false |
isBoolean: true | |
isCreditCard | 一個值,是信用卡號碼。 | 除非您的應用程式符合 PCI 標準,否則請勿將信用卡號碼儲存在您的資料庫中! 如果您想允許使用者儲存信用卡資訊,一個安全的替代方案是使用像 Stripe 這樣的支付 API。 | |
isEmail | 一個看起來像電子郵件地址的值。 | isEmail: true |
|
isHexColor | 一個字串,是十六進位顏色。 | isHexColor: true |
|
isIn | 一個值,在指定的允許字串陣列中。 | isIn: ['paid', 'delinquent'] |
|
isInteger | 一個數字,是整數(整數) | isInteger: true |
|
isIP | 一個值,是有效的 IP 地址(v4 或 v6) | isIP: true |
|
isNotEmptyString | 一個值,不是空字串 | isNotEmptyString: true |
|
isNotIn | 一個值,不在配置的陣列中。 | isNotIn: ['profanity1', 'profanity2'] |
|
isNumber | 一個值,是 Javascript 數字 | isNumber: true |
|
isString | 一個值,是一個字串(即 typeof(value) === 'string' ) |
isString: true |
|
isURL | 一個看起來像 URL 的值。 | isURL: true |
|
isUUID | 一個看起來像 UUID 的值(v3、v4 或 v5) | isUUID: true |
|
max | 一個數字,小於或等於配置的數字。 | max: 10000 |
|
min | 一個數字,大於或等於配置的數字。 | min: 0 |
|
maxLength | 一個字串,字元數不超過配置的數字。 | maxLength: 144 |
|
minLength | 一個字串,字元數至少為配置的數字。 | minLength: 8 |
|
regex | 一個字串,符合配置的正規表示式。 | regex: /^[a-z0-9]$/i |
假設您有一個屬性定義如下
workEmail: {
type: 'string',
isEmail: true,
}
當您呼叫 .create()
_或_ .update()
時,此值可以設定為任何有效的電子郵件地址(例如「[email protected]」)或空字串 ("")。但是,您無法將其設定為 null
,因為這會違反 type: 'string'
強加的類型安全限制。
若要使此屬性接受
null
(例如,如果您正在使用預先存在的資料庫),請將其變更為type: 'json'
。您通常也會想要新增isString: true
,但由於我們在此範例中已強制執行isEmail: true
,因此無需這樣做。需要記住的一個更進階的功能是,根據您的資料庫,您可以選擇利用
columnType
來告知 Sails / Waterline 在自動遷移期間要定義的欄位類型(如果相關)。
如果我們想指示屬性支援某些數字,例如星級評等,我們可能會這樣做
starRating: {
type: 'number',
min: 1,
max: 5,
required: true,
}
如果我們想讓星級評等成為選填,最簡單的方法就是移除 required: true
旗標。如果省略,starRating 將預設為零。
null
)但是,如果星級評等不總是可以是數字呢?想像一下,我們需要與舊版資料庫整合,在該資料庫中,星級評等可以是數字或特殊的 null 字面值。在這種情況下,我們希望將 starRating
屬性定義為同時支援某些數字和 null
。
為了實現這一點,只需使用 allowNull
starRating: {
type: 'number',
allowNull: true,
min: 1,
max: 5,
}
Sails 和 Waterline 屬性支援
allowNull
以提供便利,但另一個可行的解決方案是將starRating
從type: 'number'
變更為type: 'json'
。但是請記住,json
類型允許其他資料,例如布林值、陣列等。如果我們想明確防止starRating
支援這些資料類型,我們可以新增isNumber: true
驗證規則starRating: { type: 'json', isNumber: true, min: 1, max: 5, }
unique
與上面列出的所有驗證規則都不同。事實上,它根本不是一個驗證規則:它是一個資料庫層級約束。稍後詳細介紹。
如果屬性宣告自身為 unique: true
,則 Sails 會確保不允許任何兩個記錄具有相同的值。典型的範例是 User
模型上的 emailAddress
屬性
// api/models/User.js
module.exports = {
attributes: {
emailAddress: {
type: 'string',
unique: true,
required: true
}
}
};
unique
與其他驗證不同?想像一下,您的資料庫中有 1,000,000 個使用者記錄。如果 unique
的實作方式與其他驗證相同,則每次有新使用者註冊您的應用程式時,Sails 都需要搜尋一百萬個現有記錄,以確保沒有其他人已經在使用新使用者提供的電子郵件地址。這樣會非常慢,以至於當我們完成搜尋所有這些記錄時,其他人可能已經註冊了!
幸運的是,這種唯一性檢查可能是任何資料庫最通用的功能。為了利用這一點,Sails 依賴 資料庫介面卡 來實作對 unique
的支援—具體來說,它在 自動遷移 期間,將唯一性約束新增到資料庫本身中的相關欄位/欄/屬性。也就是說,當您的應用程式設定為 migrate:'alter'
時,Sails 將自動在底層資料庫中產生具有內建唯一性約束的表格/集合。一旦您切換到 migrate:'safe'
,更新您的資料庫約束就取決於您自己。
當您開始使用生產資料庫時,始終建議設定索引以提高資料庫的效能。設定索引的確切過程和最佳實務因資料庫而異,並且超出本文檔的範圍。也就是說,如果您以前從未這樣做過,請別擔心:它比您想像的要容易。
就像與您的生產架構相關的所有其他事項一樣,一旦您將應用程式設定為使用 migrate: 'safe'
,Sails 就完全將資料庫索引留給您處理。
請注意,這表示在執行手動遷移時,您應確保同時更新索引和唯一性約束。
驗證可以讓您免於編寫數百行重複的程式碼,但請記住,模型驗證是在應用程式中的每次建立或更新時執行的。在屬性定義中使用驗證規則之前,請確保您可以接受每次應用程式呼叫 .create()
或 .update()
來指定該屬性的新值時都應用它。如果情況並非如此,請編寫程式碼以在您的控制器中內聯驗證傳入的值,或在您的服務之一或模型類別方法中呼叫自訂函數。
假設您的 Sails 應用程式允許使用者透過以下任一方式註冊帳戶:(A) 輸入電子郵件地址和密碼,然後確認該電子郵件地址,或 (B) 使用 LinkedIn 註冊。您的 User
模型可能有一個名為 manuallyEnteredEmail
的屬性和另一個名為 linkedInEmail
的屬性。雖然其中一個電子郵件地址屬性是必填的,但哪個是必填的取決於使用者如何註冊。在這種情況下,您的 User
模型無法使用 required: true
驗證。為了確認已提供兩個電子郵件之一—並且提供的電子郵件有效—您必須在程式碼中相關的 .create()
和 .update()
呼叫之前手動檢查這些值
if ( !_.isString( req.param('email') ) ) {
return res.badRequest();
}
更進一步來說,假設您的應用程式接受付款。在註冊流程中,如果使用者註冊付費方案,他們必須提供用於帳單目的的電子郵件地址 (billingEmail
),而如果使用者註冊免費帳戶,他們可以跳過此步驟。在帳戶設定頁面上,付費方案的使用者會看到「帳單電子郵件」表單欄位,他們可以在其中自訂其帳單電子郵件。另一方面,免費方案的使用者會看到一個行動呼籲,其中連結到「升級方案」頁面。
雖然這些需求看起來很具體,但仍有一些未解答的問題
linkedInEmail
時,帳單電子郵件會發生什麼事?linkedInEmail
,帳單電子郵件會發生什麼事?根據這些問題的答案,我們最終可能會在 billingEmail
上保留 required
驗證,新增新的屬性(例如 hasBillingEmailBeenChangedManually
),甚至重新思考我們是否使用 unique
約束。
最後,這裡有一些提示
.update()
和 .create()
的方式。不要害怕放棄內建的驗證支援,而傾向於在您的控制器或輔助函數中手動檢查值。通常,這是最乾淨且最容易維護的方法。unique
。在開發期間,當您的應用程式配置為使用 migrate: 'alter'
時,您可以隨意新增或移除 unique
驗證。但是,如果您正在使用 migrate: safe
(例如,使用您的生產資料庫),您將需要更新資料庫中的約束/索引,以及手動遷移您的資料。盡可能地,最好在您花費大量時間實作任何後端程式碼之前,先獲取或充實您自己的應用程式使用者介面線框圖。當然,這並不總是可能的,而這就是 藍圖 API 的用途。以使用者介面為中心或「前端優先」哲學建構的應用程式更易於維護,往往錯誤更少,並且—由於以使用者體驗為核心—通常具有更優雅的 API。
您可以透過在屬性中指定 custom
函數來定義自己的自訂驗證規則。
// api/models/User.js
module.exports = {
// Values passed for creates or updates of the User model must obey the following rules:
attributes: {
firstName: {
// Note that a base type (in this case "string") still has to be defined, even though validation rules are in use.
type: 'string',
required: true,
minLength: 5,
maxLength: 15
},
location: {
type: 'json',
custom: function(value) {
return _.isObject(value) &&
_.isNumber(value.x) && _.isNumber(value.y) &&
value.x !== Infinity && value.x !== -Infinity &&
value.y !== Infinity && value.y !== -Infinity;
}
},
password: {
type: 'string',
custom: function(value) {
// • be a string
// • be at least 6 characters long
// • contain at least one number
// • contain at least one letter
return _.isString(value) && value.length >= 6 && value.match(/[a-z]/i) && value.match(/[0-9]/);
}
}
}
}
自訂驗證函數接收要驗證的傳入值作為其第一個參數。如果有效,它們應傳回 true
,否則傳回 false
。
Sails.js 開箱即用不支援自訂驗證訊息。相反,您的程式碼應查看(或「協商」).create()
或 .update()
呼叫擲出的驗證錯誤,並採取適當的措施,無論是在您的 JSON 回應中傳送特定的錯誤代碼,還是在 HTML 錯誤頁面中呈現適當的訊息。