ユーザーデータを検証するためのツールの選択に直面するとき、ルールを設定するためのインターフェイスについて頻繁に話します。 今日、宣言的なものからオブジェクトのものまで非常に多くのそのような道具があります。 各バリデーターは、表現力豊かで使いやすいものにしようとしています。 しかし、バリデータの結果であるレポートに注意を向けたいと思います。 各開発者は独自の決定を行うよう努めており、多様性がインターフェースにのみ有用である場合、その結果は逆になります。 一般的に、問題を見てみましょう。
ご注意 記事を読んだ後、お気に入りのバリデーターを捨てることができます。
現在、検証ツールは多様ですが、機能が貧弱です。 多くの場合、次の形式でエラーメッセージが表示され
。
。 これは、バグレポートの貧弱な設計の典型的な例です。 無効な文字を検出したgoコンパイラーからのメッセージを受け取ります。
test.go:16:1: invalid character U+0023 '#'
コンパイラは、エラーの場所と原因を示します。 コンパイラがメッセージでそれを置き換えると想像してください:
test.go: file should contain valid code
どうですか!? ツールからの詳細なレポートを期待し、ユーザーに情報を返すのはなぜですか。 ソースコードは、プログラムの「目で見る」ログイン値とどのように異なりますか?
現状
最も一般的なエラーレポートのリストを次に示します。
- バリデータは、文字列、配列/文字列オブジェクトを返します。
- バリデータは
true/false
返しtrue/false
(npmバリデータ)。 - バリデーターは例外をスローします。
- バリデーターは、コンソールにレポートを表示します(npm prop-types)。
このようなデータは、国際化や解釈などのさらなる使用には不適切であり、したがって役に立たない。 その結果、ライブラリは互換性がなく、システムコンポーネントは一意のビューに関連付けられています。 クライアントにレポートを送信するには、独自のラッパーを作成する必要があります。
これを修正し、一般的なレポート要件を策定してみましょう。
必要条件
今後、このオプションは数年にわたって実稼働環境で正常に機能していると言えます。
私が頼ったレポート要件は次のとおりです。
- 便利なソフトウェア処理:メッセージではなく値。
- 任意の構造のオブジェクトの表現:完全なパスを保存します。
- 便利な国際化:ルールにIDを使用します。
- 可読性:人間が読めるコードを使用します
- 移植性:レポートはランタイムまたは特定の言語に関連付けられていません。
ValidationReport
ValidationReportが登場しました-Issueオブジェクトで構成される配列。 各問題は、 path
、 rule
およびdetails
フィールドを含むオブジェクトです。
-
path
文字列または数字の配列。 オブジェクト内のフィールドのパス。 検証された値がプリミティブの場合、空になる場合があります。 -
rule
は文字列です。 エラーコード。 -
details
-オブジェクト。 エラーの原因に関する情報を含む任意のビューオブジェクト。
JavaScript:
[ { path: ['login'], rule: 'syntax', details: { pos: 1, expect: ['LETTER', 'NUMBER'], is: '$', }, }, ]
行く:
type Details map[string]interface{}; type Issue struct { Path []string `json:"path"` Rule string `json:"rule"` Details Details `json:"details"` } type Report []Issue; //... issue:= Issue{ Path: []string{"login"}, Rule: "syntax", Details: Details{ "pos": 1, "expect": []string{"LETTER", "NUMBER"}, "is": "$", }, } report := Report{issue}
このようなレポートは、他の表現に簡単に変換でき、詳細で視覚的です。 これで、
代わりに、表示できるようになります。 '$': 1
。 ネストされた構造を検証する場合、パスの管理は簡単です。
特定のエラーコードはURIとして表すことができます。
例
例として、いくつかのライブラリ関数、ログイン用のバリデーター、および関数スタイルでのJavaScriptの実装を実装します。 jsbinの準備完了コード。
ライブラリ関数
Issueの作成(createIssue)とIssue.pathの値へのプレフィックスの追加(pathRefixer)の2つのメソッドがここに実装されます。
function createIssue(path, rule, details = {}) { return {path, rule, details}; } function pathPrefixer(...prefix) { return ({path, rule, details}) => ({ path: [...prefix, ...path], rule, details, }); }
ログイン検証ツール
実際には同じログイン検証ツール。
const LETTER = /[az]/; const NUMBER = /[0-9]/; function validCharsLength(login) { let i; for (i = 0; i < login.length; i++) { const char = login[i]; if (i === 0) { if (! LETTER.test(char)) { break; } } else { if (! LETTER.test(char) && ! NUMBER.test(char)) { break; } } } return i; } function validateLogin(login) { const validLength = validCharsLength(login); if (validLength < login.length) { return [ createIssue([], 'syntax', { pos: validLength, expect: validLength > 0 ? ['NUMBER', 'LETTER'] : ['LETTER'], is: login.slice(validLength, validLength + 1), }), ]; } else { return []; } } function stringifySyntaxIssue({details}) { return `Invalid character "${details.is}" at postion ${details.pos}.`; }
実装
アプリケーションレベルの実装。 モデルをチェックする機能と、モデルを使用した抽象クエリを追加します。
function validateUser(user) { return validateSyntax(user.login) .map(pathPrefixer('login')); } function validateUsersRequest(request) { return request.users .reduce((reports, user, i) => { const report = validateUser(user) .map(pathPrefixer('users', i)); return [...reports, ...report]; }, []); } const usersRequest = { users: [ {login: 'adm!n'}, {login: 'u$er'}, {login: '&@%#'}, ], }; function stringifyLoginSyntaxIssue(issue) { return `User #${issue.path[1]} login: ${stringifySyntaxIssue(issue)}`; } const report = validateUsersRequest(usersRequest); const loginSyntaxIssues = report.filter( ({path, rule}) => path[2] === 'login' && rule === 'syntax' ); console.log(report); console.log(loginSyntaxIssues.map(stringifyLoginSyntaxIssue).join('\n'));
おわりに
ValidationReportを使用すると、さまざまなライブラリを組み合わせて検証し、裁量でプロセスを管理できます。たとえば、時間のかかるチェックを並行して実行し、結果を連結します。 異なるプログラムからのレポートも同じ方法で表示され、ハンドラーのコードを再利用できます。
実装
今日、nodejsのパッケージがあります。
- npmパッケージvalidation-report 。