MVCでは、モデルが検証を処理する必要がありますか?


25

MVCパターンを使用するために開発したWebアプリケーションを再構築しようとしていますが、検証をモデルで処理すべきかどうかはわかりません。たとえば、次のようにモデルの1つを設定しています。

class AM_Products extends AM_Object 
{
    public function save( $new_data = array() ) 
    {
        // Save code
    }
}

最初の質問:では、saveメソッドが$ new_dataの検証関数を呼び出すべきか、データが既に検証されていると仮定するのか疑問に思っていますか?

また、検証を提供する場合、データ型を定義するモデルコードの一部は次のようになると考えています。

class AM_Products extends AM_Object
{
    protected function init() // Called by __construct in AM_Object
    {
        // This would match up to the database column `age`
        register_property( 'age', 'Age', array( 'type' => 'int', 'min' => 10, 'max' => 30 ) ); 
    }
}

2番目の質問: AM_Objectのすべての子クラスは、その特定のオブジェクトのデータベース内の各列に対してregister_propertyを実行します。これが良い方法であるかどうかはわかりません。

3番目の質問:モデルで検証を処理する必要がある場合、エラーメッセージまたはエラーコードを返して、ビューでコードを使用して適切なメッセージを表示する必要がありますか?

回答:


30

最初の回答:モデルの重要な役割は、整合性を維持することです。ただし、ユーザー入力の処理はコントローラーの責任です。

つまり、コントローラーはユーザーデータ(ほとんどの場合は単なる文字列)を意味のあるものに変換する必要があります。これには解析が必要です(たとえば、異なる10進演算子などがある場合、ロケールなどに依存する場合があります)。
したがって、「データは適切に形成されていますか?」などの実際の検証は、コントローラーによって実行される必要があります。ただし、「データは理にかなっていますか?」のような検証 モデル内で実行する必要があります。

例でこれを明確にするには:
アプリケーションで、日付付きのいくつかのエンティティを追加できると仮定します(たとえば、期限に関する問題)。日付が単なるUnixタイムスタンプとして表されるAPIがありますが、HTMLページから来る場合は、異なる値のセットまたはMM / DD / YYYY形式の文字列になります。モデルにこの情報は必要ありません。各コントローラーが個別に日付を把握しようとする必要があります。ただし、日付がモデルに渡されると、モデルは整合性を維持する必要があります。たとえば、過去の日付や、休日/日曜日などの日付を許可しないことは理にかなっています。

コントローラーには入力(処理)ルールが含まれています。モデルにはビジネスルールが含まれています。何が起こっても、ビジネスルールを常に実施する必要があります。コントローラーにビジネスルールがある場合、別のコントローラーを作成した場合は、それらを複製する必要があります。

2番目の回答:このアプローチは理にかなっていますが、この方法をより強力にすることができます。最後のパラメーターを配列にする代わりに、IContstraint次のように定義されたインスタンスである必要があります。

interface IConstraint {
     function test($value);//returns bool
}

そして、数字については、

class NumConstraint {
    var $grain;
    var $min;
    var $max;
    function __construct($grain = 1, $min = NULL, $max = NULL) {
         if ($min === NULL) $min = INT_MIN;
         if ($max === NULL) $max = INT_MAX;
         $this->min = $min;
         $this->max = $max;
         $this->grain = $grain;
    }
    function test($value) {
         return ($value % $this->grain == 0 && $value >= $min && $value <= $max);
    }
}

また'Age'、正直に言うと、何を意味するのかわかりません。実際のプロパティ名ですか?デフォルトで規約があると仮定すると、パラメーターは単純に関数の最後に移動し、オプションにすることができます。設定されていない場合、デフォルトでDB列名のto_camel_caseになります。

したがって、例の呼び出しは次のようになります。

register_property('age', new NumConstraint(1, 10, 30));

インターフェースを使用することのポイントは、制約を追加することができ、必要に応じて複雑にできることです。文字列が正規表現に一致する場合。日付が少なくとも7日先であるため。等々。

3番目の回答:すべてのモデルエンティティにはのようなメソッドが必要Result checkValue(string property, mixed value)です。コントローラは、データ設定する前に呼び出す必要があります。Resultチェックが失敗したかどうかについてのすべての情報を持っており、コントローラはそれに応じてビューにそれらを伝播することができますので、それがなかった場合には、その理由を与える必要があります。
間違った値がモデルに渡された場合、モデルは単に例外を発生させて応答する必要があります。


この記事をありがとう。MVCに関する多くのことが明らかになりました。
AmadeusDrZaius

5

「back2dos」には完全には同意しません。モデルに送信する前に、コントローラーが入力データを検証するために使用できる別のフォーム/検証レイヤーを常に使用することをお勧めします。

理論的な観点からは、モデル検証は信頼できるデータ(内部システム状態)で動作し、理想的には任意の時点で繰り返し可能である必要がありますが、入力検証は信頼できないソースからのデータ(ユースケースとユーザー権限に応じて)で明示的に1回動作します。

この分離により、依存性注入によって疎結合できる再利用可能なモデル、コントローラー、およびフォームを構築できます。入力検証をホワイトリスト検証(「既知の良好を受け入れる」)、モデル検証をブラックリスト検証(「既知の不良を拒否する」)と考えてください。ホワイトリストの検証はより安全ですが、ブラックリストの検証はモデルレイヤーが非常に特定のユースケースに過度に制約されることを防ぎます。

無効なモデルデータは常に例外をスローする必要があります(そうしないと、アプリケーションは間違いに気付かずに実行を継続できます)が、外部ソースからの無効な入力値は予期せず、むしろ一般的です(間違いを犯さないユーザーがいない限り)。

参照:https : //lastzero.net/2015/11/why-im-using-a-separate-layer-for-input-data-validation/


簡単にするために、Validatorクラスファミリがあり、すべての検証が階層化された階層で行われると仮定します。具体的なバリデータの子は、電子メール、電話番号、フォームトークン、キャプチャ、パスワードなどの特殊なバリデータで構成することもできます。コントローラ入力検証は2種類ある:?1)コントローラ及び方法/コマンド、および2)データの予備審査(の存在を確認、すなわちHTTPリクエストメソッド、どのように多くのデータ入力(多すぎ少なすぎます)
アンソニーラトレッジ

入力の量が確認された後、正しいHTMLコントロールが名前で送信されたことを知っておく必要があります。HTMLフォームのすべてのコントロールが空白のままで送信されるわけではないため、リクエストごとの入力数は異なる場合があることに注意してください(特にチェックボックス)。この後、最後の予備チェックは入力サイズのテストです。私の意見では、これは遅刻ではなく早めにすべきです。コントローラーバリデーターで数量、コントロール名、および基本的な入力サイズのチェックを行うことは、コントローラーのコマンド/メソッドごとにバリデーターがあることを意味します。これにより、アプリケーションがより安全になると思います。
アンソニーラトレッジ

はい、コマンドのコントローラーバリデーターモデルメソッドに必要な引数(存在する場合)に密結合されますが、コントローラー自体はコントローラーバリデーターへの参照を保存しません。これは価値のある妥協です。なぜなら、ほとんどの入力が正当であるという前提で前進してはならないからです。アプリケーションへの違法なアクセスを早く停止できるほど、良い結果が得られます。コントローラーバリデータークラス(入力の量、名前、最大サイズ)でこれを行うと、明らかに悪意のあるHTTP要求を拒否するためにモデル全体をインスタンス化する必要がなくなります。
アンソニールトレッジ

つまり、最大入力サイズの問題に対処する前に、エンコードが適切であることを確認する必要があります。すべてを考慮すると、これは、作業がカプセル化されていても、モデルが実行するには大きすぎます。悪意のあるリクエストを拒否すると、不必要に費用がかかります。要約すると、コントローラーは、モデルに送信するものに対してより多くの責任を負う必要があります。コントローラーレベルの障害は致命的であり、200 OK以外のリクエスターへの戻り情報はありません。アクティビティを記録します。致命的な例外をスローします。すべてのアクティビティを終了します。すべてのプロセスをできるだけ早く停止します。
アンソニーラトレッジ

最小制御、最大制御、正しい制御、入力エンコード、および最大入力サイズはすべて、リクエストの性質に関係しています(何らかの形で)。一部の人々は、これら5つの中核的な事柄を、要求を尊重すべきかどうかを判断するものとして特定していません。これらすべてが満たされていない場合、なぜこの情報をモデルに送信するのですか?良い質問。
アンソニーラトレッジ

3

はい、モデルは検証を実行する必要があります。UIも入力を検証する必要があります。

有効な値と状態を決定することは、明らかにモデルの責任です。このようなルールは頻繁に変更されます。その場合は、メタデータからモデルにフィードするか、モデルを装飾します。


ユーザーの意図が明らかに悪意がある、または誤っている場合はどうでしょうか?たとえば、特定のHTTPリクエストは7つ以下の入力値を持つことになっていますが、コントローラーは70を取得します。リクエストが明らかに破損している場合、モデルにヒットする値の10倍(10x)を実際に許可するつもりですか?この場合、問題となるのはリクエスト全体の状態であり、特定の値の状態ではありません。多層防御戦略では、モデルにデータを送信する前に、HTTP要求の性質を調べる必要があることが示唆されます。
アンソニールトレッジ

(続き)この方法では、特定のユーザーが指定した値と状態が有効であることを確認するのではなく、リクエストの全体が有効であることを確認します。まだそこまでドリルダウンする必要はありません。油はすでに表面にあります。
アンソニーラトレッジ

(続き)フロントエンドの検証を強制する方法はありません。自動化されたツールは、Webアプリケーションとのインターフェイスとして使用できることを考慮する必要があります。
アンソニーラトレッジ

(考えた後)モデル内のデータの有効な値と状態は重要ですが、コントローラーを介して入ってくるリクエストの意図でヒットについて説明しました。意図の検証を省略すると、アプリケーションはより脆弱になります。意図は、良いこと(ルールに従って遊ぶこと)または悪いこと(ルールの外に出ること)のみです。意図は、入力の基本的なチェック(最小コントロール、最大コントロール、正しいコントロール、入力エンコード、最大入力サイズ)で検証できます。それはすべてか無かの命題です。すべてが成功するか、リクエストが無効です。モデルに何かを送信する必要はありません。
アンソニーラトレッジ

2

いい質問です!

World Wide Web開発に関しては、次の質問をした場合もどうでしょう。

「ユーザーインターフェースからコントローラーに不適切なユーザー入力が提供される場合、コントローラーは一種の周期的なループでビューを更新し、コマンドと入力データを処理する前に強制的に正確にする必要がありますか?条件?ビューはモデルに密結合されていますか?ユーザー入力検証はモデルのコアビジネスロジックですか、それとも予備的なものであり、コントローラー内で発生する必要がありますか(ユーザー入力データはリクエストの一部であるため)?

(実際には、良好な入力が取得されるまでモデルのインスタンス化を遅らせることができますか?

私の意見では、モデルは、モデルのインスタンス化の前(およびモデルが入力データを取得する前)に発生する基本的なHTTPリクエストの入力検証に邪魔されずに、純粋で原始的な環境を(可能な限り)管理する必要があると考えています。状態データ(永続的またはその他)とAPI関係の管理はモデルの世界であるため、コントローラーで基本的なHTTPリクエストの入力検証を行うようにします。

まとめます。

1)他の何かが進む前にコントローラーとメソッドが存在する必要があるため、ルートを検証します(URLから解析)。これは、実際のコントローラーに到達する前に、フロントコントローラーレルム(ルータークラス)で必ず発生するはずです。ああ。:-)

2)モデルには、HTTPリクエスト、データベース、ファイル、API、そしてはい、ネットワークなど、入力データの多くのソースがあります。すべての入力検証をモデルに配置する場合は、プログラムのビジネス要件の一部であるHTTP要求入力検証を検討します。ケースは閉じられました。

3)それでも、HTTPリクエストの入力が良くない場合、多くのオブジェクトをインスタンス化する費用をかけるのは近視です!** HTTP要求入力が**(良ければあなたは知ることができます要求に入って来たモデルとそのすべての複雑さ(APIおよびDB入力/出力データのためのはい、おそらく多くのバリデータ)をインスタンス化する前にそれを検証することによって)。

以下をテストします。

a)HTTPリクエストメソッド(GET、POST、PUT、PATCH、DELETE ...)

b)最小限のHTMLコントロール(十分ですか?)

c)最大のHTMLコントロール(多すぎますか?)。

d)正しいHTMLコントロール(正しいコントロールがありますか?)

e)入力エンコーディング(通常、エンコーディングはUTF-8ですか?)。

f)最大入力サイズ(いずれかの入力が範囲外にありますか?)

文字列とファイルを取得する可能性があるため、モデルがインスタンス化されるのを待つと、リクエストがサーバーにヒットするため非常に高価になる可能性があります。

ここで説明したことは、コントローラーを介して着信する要求の意図に当てはまります。意図の検証を省略すると、アプリケーションはより脆弱になります。意図は、良いこと(基本的なルールに従って遊ぶこと)または悪いこと(基本的なルールの外に出ること)のみです。

HTTPリクエストの意図は、すべてかゼロかの提案です。すべてが成功するか、リクエストが無効です。モデルに何かを送信する必要はありません。

この基本レベルのHTTPリクエストインテントは、通常のユーザー入力エラーや検証とは関係ありません。私のアプリケーションでは、HTTPリクエストが上記の5つの方法で有効である必要があります。で多層防御ならば話すの方法、あなたは、サーバー側でのユーザー入力の検証に取得することはありません任意のこれらの5つの事は失敗します。

はい、これは、ファイル入力でさえ、受け入れられた最大ファイルサイズを確認してユーザーに伝えるためのフロントエンドの試みに準拠する必要があることを意味します。HTMLのみですか?JavaScriptがありませんか?ただし、アップロードするファイルが大きすぎる場合の結果をユーザーに通知する必要があります(主に、すべてのフォームデータが失われ、システムから追い出されます)。

4)これは、HTTP要求の入力データがアプリケーションのビジネスロジックの一部ではないことを意味しますか?いいえ、それは単にコンピューターが有限のデバイスであり、リソースを賢く使用する必要があることを意味します。悪意のある活動を遅らせるのではなく、遅らせるのが理にかなっています。後で停止するのを待つために、コンピューティングリソースでより多くを支払います。

5)HTTPリクエストの入力が悪い場合、リクエスト全体が悪いです。それは私がそれを見る方法です。適切なHTTP要求入力の定義は、モデルのビジネス要件から導き出されますが、リソースの境界設定のポイントが必要です。悪いリクエストを殺して「ああ、ちょっと、気にしないで。悪いリクエスト」と言う前に、どれくらいの期間、悪いリクエストを生きさせますか。

判断は、ユーザーが合理的な入力ミスを行ったというだけではなく、HTTPリクエストが範囲外であるため、悪意があると宣言して直ちに停止する必要があります。

6)それで、私のお金のために、HTTPリクエスト(METHOD、URL /ルート、およびデータ)はすべて良いか、他は何もしないことができます。堅牢なモデルには、それ自体に関係する検証タスクが既にありますが、優れたリソースシェパードは、「自分のやり方、または高い道。正解、またはまったく来ない」と言います。

しかし、それはあなたのプログラムです。「それを行う方法は複数あります。」いくつかの方法は、他の方法よりも時間と費用がかかります。後で(モデル内で)HTTP要求データを検証すると、アプリケーションの存続期間にわたってコストが高くなります(特に、スケールアップまたはスケールアウトする場合)。

バリデータがモジュール式の場合、コントローラーでの基本的な* HTTPリクエスト入力**の検証は問題になりません。ストラテジー化されたValidatorクラスを使用するだけで、バリデーターは特殊なバリデーター(電子メール、電話、フォームトークン、キャプチャなど)で構成されることもあります。

これは完全に間違っていると見ている人もいますが、Gang of FourがDesign Patterns:Elements of Re-usable Object-Oriented Softwareを書いたとき、HTTPはまだ始まったばかりです

================================================== ========================

現在、(HTTPリクエストが有効であると見なされた後の)通常のユーザー入力検証に関係しているため、ユーザーが混乱するときにビューを更新する必要があります。この種のユーザー入力検証は、モデルで発生する必要があります。

フロントエンドでのJavaScriptの保証はありません。これは、エラーステータスでアプリケーションのUIの非同期更新を保証する方法がないことを意味します。真のプログレッシブエンハンスメントは、同期ユースケースもカバーします。

同期ユースケースの説明は、UIトリック(コントロールの表示/非表示、コントロールの無効化/有効化)の状態を追跡する時間と手間をかけたくない人もいるため、ますます失われつつあります。 、エラー表示、エラーメッセージ)をバックエンドで(通常は配列の状態を追跡することにより)。

更新:図では、Viewを参照する必要があると言いModelます。いいえ。疎結合を維持するにはView、データをからに渡す必要がありますModelここに画像の説明を入力してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.