每個 Dart 專案都有一個這樣的文件— — 你知道的。

該檔案充滿了if (value == null || value is! String || value.isEmpty)檢查。

該文件最初是“快速驗證”,但慢慢地變成了一堆沒人敢碰的意大利麵條程式碼。 🍝

我經歷過這種感覺。事實上,這正是促使我創立Eskema 的動力。

我發現大多數現有的解決方案都依賴程式碼生成,這意味著我最終得到的是固執己見、難以閱讀的樣板程式碼和神秘的生成類別。我想要的是別的東西:一個聲明式的、符合人體工學的、不固執己見的函式庫。一個能夠清楚地展示正在驗證的內容和驗證發生的位置,而不會在我的程式碼庫中堆砌一堆亂七八糟的東西。

Eskema 幾年前最初只是一個小型函數式函式庫,但後來逐漸發展壯大:現在它擁有基於類別的可靠核心、操作符糖和建構器 API。它的擴充功能仍然非常簡單;加入新的驗證器或轉換器基本上很容易,但它的功能足以滿足大多數實際的驗證需求。



為什麼選擇 Eskema?

Eskema 的主要特色包括:

  • 可組合 API:驗證器只是 Dart 函數,它接受一個值並傳回一個Result 。你可以自由地嵌套和組合它們(使用all()or()或重載的& |運算子)。這使得驗證邏輯高度可組合且易於推理。

  • 豐富的內建驗證器: Eskema 開箱即用,支援類型、數字、字串、列表、映射等驗證。例如, isString()isInt()isEmail()listEach()listIsOfLength()等等。此外,還包含存在性檢查( isNotNull()isNotEmpty()isPresent() )和比較性檢查( isGt()isLte()isIn()等)。

  • 運算子語法糖:將驗證器與& (與)和| (或)組合起來,並用not()進行取反。例如,您可以簡單地寫$isString & isNotEmpty() ,而不是all([isString(), isNotEmpty()]) 。您也可以使用>運算子內聯覆蓋錯誤訊息,例如hasLength(5) > 'must be 5 chars'

  • 可選與可空語意:預設情況下,鍵必須存在且非空。使用nullable(validator)允許欄位為null ,或optional(validator)允許欄位完全缺失。這有助於避免「無值」和「空值」之間的常見混淆。

  • 建構器 API:如果您喜歡更流暢、類型安全的風格,Eskema 提供了一個建構器 API。例如:

  final userValidator = $map().schema({
    'id': $string().trim().toIntStrict().gt(0),
    'email': $string().trim().toLowerCase().email(),
    'age': $string().toIntStrict().gte(18).optional(),
  });
  • 無需程式碼產生– 完全在執行時進行:Eskema 在執行時驗證普通的 Dart 映射和值。無需建置步驟,也無需產生類別。

  • 生產就緒:經過全面測試、文件齊全,並附有合理的錯誤訊息。錯誤訊息會產生一個結構化的期望清單(訊息、欄位路徑等),而不僅僅是一個布林值。

功能驗證範例

以下是使用功能 API 的使用者物件驗證器:

final userSchema = eskema({
  'username': isString() & isNotEmpty(),
  'password': isString() & hasLength(8, 32),
  'email': isString() & isEmail(),
  'signupDate': optional(isDateTime()),
});

final result = userSchema.validate({
  'username': 'alice',
  'password': 'secret123',
  'email': '[email protected]',
  // signupDate omitted is OK (optional)
});

if (!result.isValid) {
  print(result.expectations);
}

每個欄位都對應到一個驗證器。我們將檢查與 & 結合起來,而不是寫成all([...]) ,並使用了optional(isDateTime())來允許 signupDate 缺失。 Result 物件包含.isValid.expectations ,其中包含詳細的錯誤訊息。

如果您只需要驗證一個值:

final isNonEmptyStr = isString() & isNotEmpty();
print(isNonEmptyStr.isValid('hello'));          // true
print(isNonEmptyStr.validate('').isValid);      // false

編寫自訂驗證器也很容易:

Validator isEven = Validator((value) {
  return Result(
    isValid: value is int && value % 2 == 0,
    expected: 'even integer',
  );
});

Eskema 之前與之後

說實話:我們都曾經寫過這樣的驗證程式碼:

// Before: classic if/else soup
bool validateUser(Map<String, dynamic> user) {
  if (user['username'] == null || user['username'] is! String || user['username'].isEmpty) {
    return false;
  }

  if (user['password'] == null || user['password'] is! String) {
    return false;
  }
  if ((user['password'] as String).length < 8 || (user['password'] as String).length > 32) {
    return false;
  }

  if (user['email'] == null || user['email'] is! String) {
    return false;
  }
  final email = user['email'] as String;
  final emailRegex = RegExp(r'^[^@]+@[^@]+\.[^@]+');
  if (!emailRegex.hasMatch(email)) {
    return false;
  }

  return true;
}

這不僅冗長,而且脆弱:難以擴展,難以閱讀,而且容易出錯。

現在,Eskema 表達了同樣的邏輯:

// After: Eskema
final userSchema = eskema({
  'username': isString() & isNotEmpty(),
  'password': isString() & hasLength(8, 32),
  'email': isString() & isEmail(),
});

final result = userSchema.validate({
  'username': 'alice',
  'password': 'secret123',
  'email': '[email protected]',
});

print(result.isValid); // true

簡潔、聲明式、可組合。無需費力地處理 if/else 和正規表示式檢查,只需描述您的期望即可,Eskema 會處理剩下的事情。

使用生成器

對於那些喜歡流暢、IDE 友善方法的人來說,Eskema 的建構器 API 透過方法鏈提供了類似的功能:

**請注意**,一切都是驗證器,因此您可以組合和組成功能和建構器驗證器。

final mapValidator = $map().schema({
  'id': $string().trim().toIntStrict().gt(0),
  'tags': $list().each($string()).lengthMin(1),
});

// Usage:
final res = mapValidator.validate({'id': '42', 'tags': ['dart', 'eskema']});
print(res.isValid); // true

這與手動驗證器的結果相同,但語法更流暢。其優點在於人體工程學:IDE 自動補全、型別安全,且無需匯入大量自由函數。

人體工學和技巧

  • 使用$快捷鍵:許多零參數驗證器都有快取的別名。例如, $isString是一個預先建構的isString() 。它看起來簡潔,並且避免了重新分配函數。如果您想指定自訂錯誤訊息,請使用isString()

  • 運算子重載:熟悉&|not() 。它們使模式更簡潔,更易於閱讀。

  • 清除錯誤訊息:使用>覆蓋訊息,例如hasLength(5) > Expectation(...)

  • 無需樣板:開箱即用,適用於 Flutter 表單、API 處理程序、設定檔 - 任何您需要驗證的地方。

  • 非同步支援:需要非同步檢查?只需使用.validateAsync()即可。 Eskema 會根據需要將驗證器升級為非同步驗證。

結論

Eskema 提供了一個簡單靈活的工具包,讓您輕鬆擺脫執行時驗證的繁瑣。它功能強大,但可組合性極強。您可以驗證巢狀的 JSON、清單或單一值,語法清晰,錯誤報告豐富,無需任何程式碼產生或繁瑣的流程。

如果您的應用程式需要處理動態資料,不妨試試 Eskema。一些符合人體工學的驗證器或許能幫助您擺脫邊緣錯誤,甚至減輕編碼的痛苦。

👉 試試看: GitHub 上的 Eskema 。你未來的自己(以及你的隊友)會感謝你的。


原文出處:https://dev.to/nombrekeff/eskema-composable-ergonomic-runtime-data-validation-for-dart-done-right-2ice


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝10   💬6   ❤️11
454
🥈
我愛JS
📝1   💬5   ❤️4
88
🥉
AppleLily
📝1   💬4   ❤️1
47
#4
💬2  
6
#5
💬1  
5
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次