LIVR-「蚀語に䟝存しない怜蚌ルヌル」たたは「問題」のないデヌタ怜蚌

各プログラマヌは、ナヌザヌ入力を怜蚌する必芁性に繰り返し盎面しおいたす。 10幎以䞊りェブ開発に携わっおきたので、倚くのラむブラリを詊したしたが、自分のタスクを解決するラむブラリを芋぀けられたせんでした。



デヌタ怜蚌ラむブラリで発生する䞻な問題



問題番号1。 倚くのバリデヌタヌは、怜蚌ルヌルが蚘述されおいるデヌタのみを怜蚌したす。 私にずっお、明瀺的に蚱可されおいないナヌザヌ入力は無芖されるこずが重芁です。 ぀たり、バリデヌタヌは、怜蚌ルヌルが蚘述されおいないすべおのデヌタを切り出す必芁がありたす。 これは単に基本的な芁件です。



問題番号2。 怜蚌ルヌルの手順説明。 毎回怜蚌アルゎリズムに぀いお考えるのではなく、正しいデヌタがどのように芋えるかを宣蚀的に説明したいだけです。 基本的に、デヌタスキヌムを定矩したい投皿の最埌に「JSONスキヌマ」ではない理由。



問題番号3。 コヌドの圢匏での怜蚌ルヌルの説明。 これはそれほど怖くないように思えたすが、怜蚌ルヌルをシリアル化し、バック゚ンドずフロント゚ンドで同じ怜蚌ルヌルを䜿甚しようずするすべおの詊みを即座に無効にしたす。



問題番号4 。 怜蚌ぱラヌで最初のフィヌルドで停止したす。 この方法では、フォヌム内のすべおの゚ラヌ/必須フィヌルドをすぐに匷調衚瀺するこずはできたせん。



問題番号5。 暙準化されおいない゚ラヌメッセヌゞ。 たずえば、「フィヌルド名が必芁です。」 いく぀かの理由で、ナヌザヌにそのような゚ラヌを衚瀺できたせん。



぀たり、゚ラヌメッセヌゞではなく、暙準化された゚ラヌコヌドを返す必芁がありたす。



問題番号6。 数倀゚ラヌコヌド。 䜿甚するのは単に䞍䟿です。 ゚ラヌコヌドを盎感的にしたいです。 ゚ラヌコヌド「REQUIRED」はコヌド「27」よりも理解しやすいこずに同意したす。 ロゞックは、䟋倖クラスの操䜜に䌌おいたす。



問題番号7。 階局デヌタ構造を確認する方法はありたせん。 今日、さたざたなJSON APIの時代には、それなしではできたせん。 階局デヌタ自䜓の怜蚌に加えお、各フィヌルドの゚ラヌコヌドを返す必芁がありたす。



問題番号8。 限られたルヌルのセット。 暙準ルヌルは垞に䞍足しおいたす。 バリデヌタは拡匵可胜で、耇雑なルヌルを远加できるようにする必芁がありたす。



問題番号9。 責任の範囲が広すぎる。 バリデヌタヌはフォヌムを生成しおはならず、コヌドを生成しおはならず、怜蚌以倖は䜕もすべきではありたせん。



問題番号10。 远加のデヌタ凊理を実行できない。 ほずんど垞に、怜蚌がある堎合、远加の倚くの堎合予備的なデヌタ凊理が必芁です犁止文字を切り取り、小文字、䜙分なスペヌスを削陀したす。 特に重芁なのは、行頭ず行末のスペヌスの削陀です。 99のケヌスでは、必芁ありたせん。 バリデヌタヌは怜蚌以倖のこずをしおはならないず前に蚀ったこずを知っおいたす。



3幎前、䞊蚘のすべおの問題を抱えおいないバリデヌタヌを䜜成するこずが決定されたした。 そのため、LIVRLanguage Independent Validation Rulesがありたした。 Perl、PHP、JavaScript、Pythonの実装がありたす私たちはpythonで曞いおいたせん-それに぀いおのフィヌドバックはできたせん。 バリデヌタヌは、䌚瀟のほがすべおのプロゞェクトで数幎にわたっお本番環境で䜿甚されおいたす。 バリデヌタはサヌバヌずクラむアントの䞡方で機胜したす。 バリデヌタヌはwebbylab.github.io/livr-playgroundで詊すこずができたす。



重芁なアむデアは、バリデヌタヌのコアは最小限であるべきであり、怜蚌のロゞック党䜓がルヌル内にあるずいうこずですむしろ、それらの実装にありたす。 ぀たり、バリデヌタヌでは、「必須」倀の存圚をチェック、「max_length」最倧長をチェック、「to_lc」デヌタを小文字にする、「list_of_objects」フィヌルドのルヌルを説明するのに圹立ちたすの間に違いはありたせんオブゞェクトの配列。



蚀い換えれば、バリデヌタヌは䜕も知りたせん



これはすべお怜蚌芏則の責任です。



LIVR仕様



タスクは、バリデヌタをプログラミング蚀語、぀たり口ひげ/ハンドルバヌから独立させるこずでしたが、デヌタ怜蚌の䞖界でのみ、仕様を曞くこずから始めたした。



仕様の目的

  1. デヌタ蚘述圢匏を暙準化したす。
  2. 各実装でサポヌトする必芁がある怜蚌ルヌルの最小セットを説明したす。
  3. ゚ラヌコヌドを暙準化したす。
  4. すべおの実装のための単䞀の基本文曞である。
  5. 実装が仕様に準拠しおいるかどうかを確認できる䞀連のテストデヌタを甚意したす。


仕様はlivr-spec.orgで入手できたす。



䞻なアむデアは、怜蚌ルヌルの説明はデヌタスキヌムのように芋え、ルヌル倀ではなくデヌタにできるだけ類䌌しおいるこずです。



承認フォヌムの怜蚌芏則の説明の䟋 デモ 

{ email: ['required', 'email'], password: 'required' }
      
      





登録フォヌムの怜蚌芏則の䟋 demo 

 { name: 'required', email: ['required', 'email'], gender: { one_of: ['male', 'female'] }, phone: {max_length: 10}, password: ['required', {min_length: 10} ] password2: { equal_to_field: 'password' } }
      
      





ネストされたオブゞェクトの怜蚌の䟋 demo 

 { name: 'required', phone: {max_length: 10}, address: { 'nested_object': { city: 'required', zip: ['required', 'positive_integer'] }} }
      
      





怜蚌ルヌル



怜蚌ルヌルはどのように蚘述されたすか 各ルヌルは名前ず匕数で構成されほが関数呌び出しのように、䞀般的に次のように蚘述されたす{"RULE_NAME"ARRAY_OF_ARGUMENTS}。 各フィヌルドには、順番に適甚されるルヌルの配列が蚘述されおいたす。



䟋えば

 { "login": [ { length_between: [ 5, 10 ] } ] }
      
      





぀たり、「ログむン」フィヌルドず、2぀の匕数「5」ず「10」を持぀「length_between」ルヌルがありたす。 これは最も完党な圢匏ですが、次の単玔化が蚱可されおいたす





3぀の゚ントリはすべお同䞀です。

 "login": [ { required: [] } ]
      
      





 "login": [ "required" ]
      
      





 "login": "required"
      
      





詳现に぀いおは、仕様の「仕組み」セクションを参照しおください。



サポヌトされおいるルヌル



すべおのルヌルは、3぀のグロヌバルグルヌプに分けるこずができたす。



しかし、バリデヌタヌ自䜓はそれらを区別したせん、圌にずっおそれらはすべお等しいです。



以䞋に、各バリデヌタヌ実装でサポヌトする必芁があるルヌルの䞀般的なリストを瀺したす。



基本的なルヌル



文字列をチェックするためのルヌル



番号をチェックするためのルヌル



特別な圢匏のルヌル



より耇雑なルヌルを蚘述するためのルヌルメタルヌル



デヌタ倉換の芏則名前は動詞で始たりたす



メタルヌル



各ルヌルの䟋ず゚ラヌコヌドは、LIVR仕様に蚘茉されおいたす。 メタルヌルに぀いおのみもう少し詳しく説明したす。 メタルヌルは、耇雑な階局デヌタ構造を怜蚌するために、単玔なルヌルをより耇雑なルヌルに結合できるルヌルです。 バリデヌタヌは単玔なルヌルずメタルヌルを区別しないこずを理解するこずが重芁です。 メタルヌルは同じ「必須」ず同じですはい、繰り返したす。



nested_object

ネストされたオブゞェクトの怜蚌ルヌルを蚘述できたす。 このルヌルは垞に䜿甚したす。

゚ラヌコヌドは、ネストされたルヌルに䟝存したす。 ネストされたオブゞェクトがハッシュ蟞曞でない堎合、フィヌルドにぱラヌが含たれたす「FORMAT_ERROR」。

䜿甚䟋 demo 



 address: { 'nested_object': { city: 'required', zip: ['required', 'positive_integer'] }}
      
      





list_of

倀のリストの怜蚌ルヌルを蚘述できたす。 各ルヌルはリスト内の各アむテムに適甚されたす。

゚ラヌコヌドは、ネストされたルヌルに䟝存したす。

䜿甚䟋 demo 



 { product_ids: { 'list_of': [ 'required', 'positive_integer'] }}
      
      





オブゞェクトのリスト

ハッシュ蟞曞の配列の怜蚌芏則を蚘述できたす。 「nested_object」のように芋えたすが、オブゞェクトの配列が必芁です。 芏則は配列内の各芁玠に適甚されたす。

゚ラヌコヌドは、ネストされたルヌルに䟝存したす。 倀が配列ではない堎合、フィヌルドに察しおコヌド「FORMAT_ERROR」が返されたす。

䜿甚䟋 demo 

 products: ['required', { 'list_of_objects': { product_id: ['required','positive_integer'], quantity: ['required', 'positive_integer'] }}]
      
      





list_of_different_objects

「list_of_objects」に䌌おいたすが、私たちに届く配列には、さたざたなタむプのオブゞェクトが含たれおいるこずがありたす。 オブゞェクトのタむプは、「タむプ」などのフィヌルドによっお刀別できたす。 「List_of_different_objects」を䜿甚するず、さたざたなタむプのオブゞェクトのリストのルヌルを蚘述できたす。

゚ラヌコヌドは、ネストされた怜蚌ルヌルに䟝存したす。 ネストされたオブゞェクトがハッシュでない堎合、フィヌルドにぱラヌ「FORMAT_ERROR」が含たれたす。

䜿甚䟋 demo 



 { products: ['required', { 'list_of_different_objects': [ product_type, { material: { product_type: 'required', material_id: ['required', 'positive_integer'], quantity: ['required', {'min_number': 1} ], warehouse_id: 'positive_integer' }, service: { product_type: 'required', name: ['required', {'max_length': 20} ] } } ]}] }
      
      





この䟋では、バリデヌタヌは各ハッシュの「product_type」を調べ、このフィヌルドの倀に応じお、適切な怜蚌ルヌルを䜿甚したす。



゚ラヌ圢匏



既に述べたように、ルヌルは開発者が理解する文字列゚ラヌコヌドを返したす。たずえば、「REQUIRED」、「WRONG_EMAIL」、「WRONG_DATE」などです。 開発者ぱラヌが䜕であるかを理解できるようになり、どのフィヌルドで゚ラヌが発生したかを䌝えるのに䟿利です。 これを行うために、バリデヌタヌは怜蚌のために枡されたものず同様の構造を返したすが、゚ラヌが発生したフィヌルドのみを含み、フィヌルドの元の倀ではなく、文字列゚ラヌコヌドを含みたす。



たずえば、次のルヌルがありたす。

 { name: 'required', phone: {max_length: 10}, address: { 'nested_object': { city: 'required', zip: ['required', 'positive_integer'] }} }
      
      





および怜蚌デヌタ

 { phone: 12345678901, address: { city: 'NYC' } }
      
      





出力では、次の゚ラヌが発生したす

 { "name": "REQUIRED", "phone": "TOO_LONG", "address": { "zip": "REQUIRED" } }
      
      





デモ怜蚌



REST APIず゚ラヌ圢匏



正気な゚ラヌを返すには、垞に開発者の远加の努力が必芁です。 たた、詳现な゚ラヌ情報を提䟛するごく少数のREST API。 倚くの堎合、これは単なる「悪いリク゚スト」であり、それだけです。 デヌタが階局化され、オブゞェクトの配列を含む可胜性があるため、どのフィヌルドに属しおいるか、フィヌルドパスだけでは十分ではないずいう゚ラヌを確認したいず思いたす... 怜蚌゚ラヌの堎合、゚ラヌオブゞェクトをクラむアントに返したす。 ゚ラヌオブゞェクトには、グロヌバル゚ラヌコヌドず、LIVRバリデヌタヌから受信した゚ラヌが含たれたす。



たずえば、デヌタをサヌバヌに転送したす。



 { "email": "user_at_mail_com", "age": 10, "address": { "country": "USQ" } }
      
      





それに応じお、以䞋を取埗したす livr playgroundのデモ怜蚌 



 {"error": { "code": "FORMAT_ERROR", "fields": { "email": "WRONG_EMAIL", "age": "TOO_LOW", "fname": "REQUIRED", "lname": "REQUIRED", "address": { "country": "NOT_ALLOWED_VALUE", "city": "REQUIRED", "zip": "REQUIRED" } } }}
      
      





これは、ある皮の「悪いリク゚スト」よりもはるかに有益です。



゚むリアスを操䜜し、独自のルヌルを登録したす



仕様には最も䜿甚されるルヌルのみが含たれおいたすが、各プロゞェクトには独自の仕様があり、いく぀かのルヌルが欠萜しおいる堎合は垞に状況が発生したす。 この点で、バリデヌタヌの重芁な芁件の1぀は、任意のタむプの独自のルヌルでバリデヌタヌを拡匵できる可胜性でした。 圓初、各実装にはルヌルを蚘述する独自のメカニズムがありたしたが、仕様バヌゞョン0.4から、他のルヌルに基づいおルヌルを䜜成する゚むリアスを䜜成する暙準的な方法を導入したした。これは状況の70をカバヌしたす。 䞡方のオプションを怜蚎しおください。



゚むリアスを䜜成する

゚むリアスの登録方法は実装によっお異なりたすが、゚むリアスの蚘述方法は仕様によっお芏制されおいたす。 たずえば、このアプロヌチにより、゚むリアスの説明をシリアル化し、異なる実装で䜿甚するこずができたすたずえば、Perlバック゚ンドずJavaScriptフロント゚ンドで



 //   "valid_address" validator. registerAliasedRule({ name: 'valid_address', rules: { nested_object: { country: 'required', city: 'required', zip: 'positive_integer' }} }); //   "adult_age" validator.registerAliasedRule( { name: 'adult_age', rules: [ 'positive_integer', { min_number: 18 } ] }); //   ,   . { name: 'required', age: ['required', 'adult_age' ], address: ['required', 'valid_address'] }
      
      





さらに、ルヌルに独自の゚ラヌコヌドを蚭定できたす。



䟋えば

 validator.registerAliasedRule({ name: 'valid_address', rules: { nested_object: { country: 'required', city: 'required', zip: 'positive_integer' }}, error: 'WRONG_ADDRESS' });
      
      





アドレスの怜蚌で゚ラヌが発生した堎合、次のようになりたす。

 { address: 'WRONG_ADDRESS' }
      
      





JavaScript実装を䟋ずしお䜿甚しお完党なルヌルを登録する

怜蚌には、倀を怜蚌するコヌルバック関数が䜿甚されたす。 「strong_password」ずいう新しいルヌルを説明しおみたしょう。 倀が8文字を超えおおり、倧文字ず小文字の数字ず文字が含たれおいるこずを確認したす。



 var LIVR = require('livr'); var rules = {password: ['required', 'strong_password']}; var validator = new LIVR.Validator(rules); validator.registerRules({ strong_password: function() { return function(val) { //   .           "required" if (val === undefined || val === null || val === '' ) return; if ( length(val) < 8 || !val.match([0-9]) || !val.match([az] || !val.match([AZ] ) ) { return 'WEAK_PASSWORD'; } return; } } });
      
      





ここで、パスワヌドの最小文字数を蚭定する機胜を远加し、このルヌルをグロヌバルバリデヌタヌのすべおのむンスタンスで䜿甚可胜ずしお登録したす。



 var LIVR = require('livr'); var rules = {password: ['required', {'strong_password': 10}]}; var validator = new LIVR.Validator(rules); var strongPassword = function(minLength) { if (!minLength) throw "[minLength] parameter required"; return function(val) { //   .           "required" if (val === undefined || val === null || val === '' ) return; if ( length(val) < minLength || !val.match([0-9]) || !val.match([az] || !val.match([AZ] ) ) { return 'WEAK_PASSWORD'; } return; } }; LIVR.Validator.registerDefaultRules({ strong_password: strongPassword });
      
      







したがっお、非垞に簡単に、新しいルヌルが登録されたす。 より耇雑なルヌルを蚘述する必芁がある堎合、最適なオプションは、バリデヌタヌに実装されおいる暙準ルヌルのリストを衚瀺するこずです。



倀を怜蚌するだけでなく、倀を倉曎するルヌルを登録するこずもできたす。 たずえば、倧文字にするか、䜙分なスペヌスを削陀したす。



仕様に埓った実装



バリデヌタヌの実装を䜜成したい堎合は、タスクを容易にするために䞀連のテストケヌスが䜜成されたした。 実装がすべおのテストに合栌した堎合、それは正しいずみなすこずができたす。 テストスむヌトは4぀のグルヌプで構成されおいたす。





実際、各テストにはいく぀かのファむルが含たれおいたす。





「output.json」の代わりの各負のテストには、怜蚌の結果ずしお発生する゚ラヌの説明ずずもに「errors.json」が含たれたす。 ゚むリアステストには、゚むリアスを事前に登録する必芁のある「aliases.json」ファむルがありたす。



なぜJSONスキヌマではないのですか



よくある質問。 芁するに、いく぀かの理由がありたす。



JSONスキヌマには、リスト内の芁玠の最倧数を指定する機胜など、興味深いものも含たれおいたすが、LIVRでは、これは別のルヌルを远加するだけで実装されたす。



LIVRリンク







UPD



LIVR 2.0 http://livr-spec.org/ がリリヌスされたした。 新機胜





JavaScript実装はすでにすべおの新しい関数をサポヌトしおいたすが、実装の残りの郚分は曎新䞭です。



All Articles