ウィザードを使用したWebページのREST API設計


11

ウィザード形式のWebページがあります。APIへの送信ボタンは、ウィザードの4番目のステップにあります。ただし、ウィザードの次のステップに進む前に、入力したデータをデータベースに保存しておく必要があります。また、REST APIが単一のタブを持つページで機能するようにしたいと考えています。

そこで、クエリパラメータaction = draftまたはsubmitを実行するようにAPIを設計しました。アクションがドラフトの場合、特定のフィールドのみが必須です。アクションが送信の場合、すべてのフィールドが必須です。REST APIのサービスレイヤーでの検証は、クエリパラメーターに基づいて行われます。ドキュメントでif / else句を明示的に指定する必要があるようです。これはRESTfulな設計の受け入れ可能な形式ですか?これらの要件に最適な設計は何ですか?


3
中間データをDBに保存する必要があるのはなぜですか?
Dan1701

2
@ Dan1701:別のマシンからウィザードを再開できます。長く複雑なフォームに入力する場合、ユーザーが必要なデータをすべて準備していない場合や、ユーザーが別の場所からアップロードするために追加のファイルを収集する必要がある場合があるため、必要なすべてのデータが完了するまで数日かかることがあります。あなたが別のデバイスから再開することができた場合は、など、携帯電話から写真をアップロードし、デスクトップ上の実際のキーボードで長い説明/引数の入力を続行するために、ウィザードを読み込むことができます
リー・ライアン

その場合、@ guillaume31の答えは理にかなっていると思います。
Dan1701 2016

回答:


7

ウィザードのステップ間でサーバー上に物事を永続化したいので、各ステップを個別のリソースと見なすことは完全に許容できるようです。これらの線に沿った何か:

POST /wizard/123/step1
POST /wizard/123/step2
POST /wizard/123/step3

応答にハイパーメディアリンクを含めることで、このステップの後にクライアントが実行できることをクライアントに通知できます。その例を図5に示します


私はUIにAngularを使用しています。したがって、ステートマシンがどれほど役立つかはわかりません。しかし、ステップベースのリソースは、別のテーブルを管理するよりも意味があるように思えます。また、すべてを1つのステップで送信できるはずです。今日、このデザインを試してみましょう。助けてくれてありがとう。
TechCrunch 2016

どういたしまして。ちなみに、「2つのテーブル」アプローチは、これと相互に排他的ではありません。ステップごとに1つのHTTPリソースがあることは、データベーススキーマはもちろんのこと、アプリケーションサーバー上のオブジェクトモデルを決定するものではありません。これは単なるWeb表現です。
guillaume31、2016

1
@TechCrunch基本的にギヨームとは、フォームを表すオブジェクト/テーブルをパーツに分割できることを意味し、モデルの一部は各ステップで保存されます。実際、各「ステップ」をモデル全体の一部のフォームにすることができます。そして、あなたがこのアプローチをとれば、それは実際にアーキテクチャを信じられないほど単純にします。サーバーへの各POSTは同じモデルを(作成または)更新し、各GETは同じモデルをロードします。各ステップは、意味的に意味のある一連のフィールドに入力するフォームになります(一緒に属します)。また、in_progressまたはのモデルにブール値を指定するだけdraftです。
Chris Cirefice 2016

3

私は少し前に同様のことをする必要がありました、そして以下は私たちが最終的に何をするかを説明しています。

ItemとUnfinishedItemの2つのテーブルがあります。ユーザーがウィザードでデータを入力すると、データはUnfinishedItemテーブルに保存されます。ウィザードの各ステップで、サーバーはそのステップ中に入力されたデータを検証します。ユーザーがウィザードを終了すると、ウィザードは非表示の読み取り専用フォームを、送信されるすべてのデータを示す確認ページにレンダリングします。ユーザーはこのページを確認し、関連する手順に戻ってエラーを修正できます。ユーザーが入力に満足したら、ユーザーは送信をクリックし、ウィザードは非表示/読み取り専用フォームフィールドのすべてのデータをAPIサーバーに送信します。APIサーバーはこのリクエストを処理するときに、ウィザードの各ステップで行ったすべての検証を再実行し、個々のステップに適合しない追加の検証を実行します(たとえば、グローバル検証、高価な検証)。

2つのテーブルアプローチの利点:

  • データベースでは、UnfinishedItemテーブルよりもItemテーブルに厳しい制約を課すことができます。ウィザードの終了時に実際に必要になるオプションの列を用意する必要はありません。

  • UnfinishedItemsを除外することを覚えておく必要がないので、レポート用に完成したItem全体のクエリを集約する方が簡単です。今回の場合、ItemとUnfinishedItemsの間で集計クエリを実行する必要がなかったため、これは問題になりません。

欠点:

  • 検証ロジックが重複する傾向があります。私たちが使用したWebフレームワークであるDjangoは、ちょっとしたメタマジックを伴うモデル継承を使用して、ItemとUnfinishedItemで異なる必要がある制約を変更するので、これをもう少し耐えやすくします。Djangoはモデルからデータベースとフォームの検証のほとんどを生成します。その上に追加の検証をいくつか追加するだけで済みます。

私が検討したその他の可能性と、なぜ私たちがそれらを採用しなかったのか:

  • Cookieまたはローカルストレージにデータを保存する:ユーザーは別のデバイスからの送信を続行できないか、ブラウザの履歴を削除した場合
  • UnfinishedItemを非構造化データ(JSONなど)としてデータベースまたはセカンダリデータストアに保存します。解析ロジックを定義する必要があり、Djangoの自動モデル/フォーム検証を使用できません。
  • クライアント側でステップごとの検証を行います。Python/ DjangoとJavaScriptの間で検証ロジックを複製する必要があります。

1
「ドラフト」タイプのモデルと「完成」モデルの検証を指摘するための+1。それは考えていませんでしたし、考慮すべき重要な点です。それ以外の場合はif、検証全体を通してドラフトのステータスをチェックするステートメントがたくさんあるでしょう。Ruby on Railsのような一部の非常に洗練されたフレームワークは、正しく実装されていればその問題を大幅に簡略化できます。
Chris Cirefice 2016

1

@ guillauma31と@Lie Ryanのソリューションを組み合わせた場合と同様の方法でこれを実装しました。

主な概念は次のとおりです。

  1. 完了するまで部分的に保持される可能性のある3ステップのウィザードがあります。
  2. 各ステップは、(例えば:それ自身のリソースを持っている/users/:id_user/profile/step_1.../step_2など)
  3. 各ステップで、GETリクエストを通じてデータと完了ステータスを取得し、PATCHリクエストを通じて永続化できます。
  4. 各リソースには、入力されたデータに対する独自の検証ルールがあります。
  5. 各ステップは、シーケンスを保証するために次のステップの入力で使用する必要があるキーを返します。使用されるか、新しいトークンが生成されると、このトークンは期限切れになります。
  6. 最後のステップで、データベースに必要なデータがすべて揃い、確認画面が表示されます。この確認では、別のリソースを呼び出してデータに完了のマークを付けます(例:).../profile/confirm。このリソースは、すべてのデータを再度受信する必要はありません。これは、データに正しい完全なマークを付けるだけです。
  7. 数日以上あるこの不完全なエントリを消去する定期的なルーチンがあります。

フロントエンドの人たちは、ウィザードの前後のフローを機能させるためにトークンを処理する必要があります。

APIはステートレスでアトミックです。

この設定で「ワンステップウィザード」を機能させるには、トークンフローを削除する、ウィザードのタイプに基づいてトークンを返すリソースを作成する、またはこの特定の1つだけを満たすために新しいリソースを作成するなど、いくつかの点を変更する必要があります。ステップウィザード(などPUT /users/:id_user/profile/)。

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