web-dev-qa-db-ja.com

REST API入力検証戦略

REST APIを実装しており、CREATEおよびUPDATEエンドポイントのJSON API入力を検証する必要があります。入力が無効な場合は、処理を実行する前に400エラーを送信することが目標です。

過去には、ほとんどの人と同様に、Marshmallow/Cerberus/Schema/Voluptuous/Schematicsなどのスキーマ検証ツールを使用しています(ここではPythonを使用しています)

しかし、私のDBモデル自体に、データベース内のデータの一貫性を保証するためのバリデーターや制約を含めることができます(すべきですか?)。

それを念頭に置いて、レコード作成の私の一般的な戦略は次のようになります。

create_record(inputs):
    errors = validate_schema(inputs)
    if errors:
        send_400(errors)

    try:
        record = create_record(inputs)
    catch:
        send_500()

    return record

だから私はいくつかの潜在的な欠点がある2つの場所である種のデータ検証を実行する必要があるように思えます:

  • 2つの異なる場所でコードを維持する必要がある
  • スキーマ検証がDBバリデーター/制約よりも許容度が高い場合、検証は成功しますが、レコードの作成は引き続き失敗し、400ではなく500になります。

これに推奨されるいくつかのDRY戦略はありますか?

1
nbarraille

構文的に正しい(ここではHTTP 400は誤解を招く)ために400を使用しないことをお勧めしますが、意味的に正しくない(別名invalid)リクエストです。しかし、それはここでのトピックではありません。

問題は、検証[〜#〜] dry [〜#〜]についてです。

私は、ここに従うべき黄金律はないと思います。最新のWebアプリケーションを実行している場合、二重簿記[〜#〜] dry [〜#〜]と保守性の間にはトレードオフがあります。

典型的なスタックは:

  • ブラウザのフロントエンド(ある種の検証を実装する必要がある)
  • バックエンド(ある種の検証を実装する必要がある)

そしてあなたのケースでは、あなたのコードとあなたのDBに検証があります。

使いやすさのために-ユーザーの観点から-クライアントに検証レイヤーを用意し、対話的にエラーを示し、入力を改善する方法についてアドバイスを提供することは理にかなっています。エラーが発生したため、Formに記入してサーバーに送信し、すべてを再入力する必要があった時代は終わりました。

とはいえ、クライアントとサーバーの間には常に-少なくともある種の-二重の簿記があります。 ビジネスロジックを1か所に保ち、コードジェネレーターを使用してクライアント側とサーバー側のコードを生成し、防止する[ 〜#〜] dry [〜#〜]少なくとも論理的な観点から。これは、ソフトウェアを維持するのが難しく、地獄に終わりました。

バックエンドで[〜#〜] dry [〜#〜]についての質問に来ます。検証ロジックを2つの部分に分割することには意味があります。

1)データベースが最もうまく処理できるもの:

  • 制約("これはユニークですか?"、"これは既知のものを参照します")
  • データ型("これは本当にdate?")
  • (おそらくデータアクセス-DBユーザーと同等のユーザーを実装する場合)

2)コードが最も得意とすること:

  • 郵便番号の検証
  • 完全性などの確認.

これにより、データアクセス層から発生するエラーのクラスがおよそ2つになります。

  • 技術的なエラー(DBがなくなった
  • 意味的エラー(一意のキー制約違反

後者はvalidation errorに変換されます。

datatypesの場合、ある種の二重チェックが発生します。何かが日付かどうか-それは無視できるでしょう。そして、残りは直交/比較的DRYです。

[〜#〜] but [〜#〜]一貫性要件に依存しますデータベースを一種の最後の防御線データベースだけでなくコードにも検証ロジックを実装します(これにより、[〜#〜] dry [〜#〜])。

3
Thomas Junk

ドライな方法は、存在する可能性のある検証エラーの種類ごとに検証を行うのではなく、create_recordからの複数の種類のエラーを処理することです。例えば

try:
  record = create_record(inputs)
catch InputsValidationError:
  send_400()
catch:
  send_500()
0
Cormac Mulhall