4.3 - ビューにパラメータを渡す
ビューにパラメータを渡す
このセクションでは、ToDo 項目を作成し、ビューを通してそれを保存するために、2 つのモジュールを融合させる初期設定について説明します。そのためには、ToDo項目を作成するビューに、ToDo項目のタイトルを渡す必要があります。ビューにデータを渡すには、次のようなルートを使用します。
- URL
- データおよびパラメータは、リクエストのURLの内部で渡すことができます。実装が簡単なため、一般的にシンプルなケースで使用されます。
- ボディ
- リクエストボディの異なるフィールドにデータを格納することができます。これは、より複雑なデータ構造や大きなペイロードの場合に使用されます。
- ヘッダー
- リクエストヘッダの異なるフィールドにデータを格納することができます。これは、送信されるリクエストに関するメタデータを格納するために使用されます。また、リクエストの認証データもヘッダーに格納します。
これらのアプローチについては、プロジェクトを通して説明しますが、今は、最も簡単な導入方法であるURLメソッドを使ってデータを渡すことにします。まず、以下のレイアウトでToDoビューの構造を作成します。
└── views
├── auth
│ ├── login.rs
│ ├── logout.rs
│ └── mod.rs
├── mod.rs
└── to_do
├── create.rs
└── mod.rs
ToDoビューをTo_doモジュールの隣にある独自のビューモジュールに配置したことがわかります。このようにビューを積み重ねていくことで、サーバーからビューを出し入れしたり、必要に応じて他のプロジェクトにビューを組み込むことができるようになります。今のところ、ToDoアイテムの作成は、以下のような形で行います。
図4.2 「ToDo項目作成の流れ
図4.2で示した処理を実行するためには、以下の手順が必要です。
- ToDo項目リストの現在の状態を読み込む。
- URLから新しいToDoアイテムのタイトルを取得する。
- タイトルとステータスのpendingをto_do_factoryに渡します。
- 前のステップの結果を、文字列createと状態とともにプロセス・モジュール・インターフェースに渡す。
- プロセスが終了したことを知らせる文字列をユーザーに返す。
views/to_do/create.rsファイルで先に定義したこれらのステップを実行することができます。まず、以下のコードで必要なものをインポートする必要があります。
use serde_json::value::Value;
use serde_json::Map;
use actix_web::HttpRequest;
use crate::to_do::{to_do_factory, enums::TaskStatus};
use crate::state::read_file;
use crate::processes::process_input;
serde_json::value::Value と serde_json::Map を使用して、 state.json ファイルから読み込むデータの種類を定義し、HttpRequest 構造体を使用して URL からタイトルを抽出することになります。次に、他のモジュールから必要なものをインポートして、アイテムの作成、状態ファイルの読み取り、入力の処理を行います。ビューは、次のコードで定義できます。
pub async fn create(req: HttpRequest) -> String {
let state: Map<String, Value> = read_file("./state.json"); // step 1
let title: String = req.match_info()
.get("title")
.unwrap()
.to_string(); // step 2
let item = to_do_factory(&title.as_str(), TaskStatus::PENDING); // step 3
process_input(item, "create".to_string(), &state);
// step 4
return format!("{} created", title) // step 5
}
これはサーバーが処理しているビューであるため、非同期関数であることを覚えておく必要があります。また、match_info関数によってHttpRequestからタイトルが抽出されていることがわかります。URLにタイトルがない場合、アイテムを作成し、抽出したタイトルをStringに変換する処理を続けたくないので、直接アンラップする必要があります。次に、この参照をto_do_factoryに渡してItemTypes enumを作成する必要があります。そして、この列挙型にコマンドと現在のアプリケーションの状態への参照を添えてprocess_input関数に渡しますが、この関数では、渡されたコマンドとアイテムの種類に基づいて、状態をどのように処理するか、一連の手順を実行します。しかし、アイテムを処理するためのロジックは、このビューには含まれていないことに注意してください。これは、コード直交性(Code Orthogonality)の懸念事項の分離と呼ばれるものです。コードの直交性とは、次のような数学的概念を指します。
図4.3 - 直交性の数学的概念
図4.3を見ると、あるベクトルが他のベクトルに直交している場合、そのベクトルは他のベクトルに投影されないと言われていることがわかります。物理学では、ベクトルが力であれば、これらのベクトルは互いに影響を及ぼさないことになります。Processsモジュールのコードを削除すると、createビューを参照しなければならないので、その影響を受けることになります。しかし、プロセスのロジックは、create viewで定義すべきではありません。これは、他の場所でプロセスを使用する必要があるためでもありますが、これがすべてではありません。createビューを見ると、アプリケーションの他の部分との関係で、保留アイテムの作成に関するロジックを見ることができます。このため、開発者は何が起こっているのかを正確に把握しやすくなっています。このセクションで先述した、ToDo項目を作成するための5つのステップに関係のない詳細に迷わされることはないのです。もし開発者がアイテムの保存に関するロジックを調べたい場合は、これを定義したファイルを調査することができます。
ここで、to_doモジュールのビューを外部から利用できるようにする必要があります。そのためには、views/to_do/mod.rs ファイルに以下のコードで to-do views factory 関数を作成します。
mod create;
use actix_web::web::{ServiceConfig, get, scope};
pub fn to_do_views_factory(app: &mut ServiceConfig) {
app.service(scope("v1/item")
.route("create/{title}", get().to(create::create))
);
}
先のコードでは、create viewをpublicにしていませんが、ファクトリーでこれを使用してビューを定義していることがわかります。また、URLに渡されるタイトルを、/{title}で定義しています。タグを追加する必要があります。アイテムビューが機能するようになったので、to_do_views_factoryをアプリケーションのviews/mod.rsファイルに次のコードでプラグインする必要があります。
mod auth;
mod to_do; // define the module
use auth::auth_views_factory;
use to_do::to_do_views_factory; // import the factory
use actix_web::web::ServiceConfig;
pub fn views_factory(app: &mut ServiceConfig) {
auth_views_factory(app);
to_do_views_factory(app); // pass the ServiceConfig
}
先のコードでは、モジュールを定義し、ファクトリーをインポートし、アプリケーションの設定を渡していることがわかります。これで、アプリケーションを実行し、ToDo項目を作成する準備ができました。アプリケーションを実行すると、次のURLでアイテムを作成することができます。
図4.4-ToDo項目を作成するためのビュー
コンソールを見ると、次のように出力されています。
ルートにあるstate.jsonファイルを見ると、以下のようなデータが出てきます。
ToDo項目を作成するプロセスがうまくいったことがわかります。このアプリケーションは、URLからタイトルを取り込み、保留中のToDo項目を作成し、JSONファイルに保存しています。これは画期的なことですが、JSONファイルはデータの保存に最適なソリューションではないことに注意する必要があります。しかし、第6章「PostgreSQLによるデータ永続化」で適切なデータベースを構成するため、今はこれで十分です。また、URLの中に%20が見えますが、これはスペースを表しています。このスペースは、コンソールのプリントアウトとJSONファイルへのデータの保存を意味し、このスペースはブラウザに表示されるビューにも含まれていることがわかります。URLからToDoのタイトルを取り込み、端末にプリントアウトしてブラウザに表示し、JSONファイルに保存しているのです。ユーザーにデータを表示し、ファイルに保存することができるので、実質的にWebアプリケーションの基礎ができたことになります。
GETメソッドは私たちにとって有効ですが、ToDoアイテムの作成には最も適したメソッドではありません。GETメソッドは、キャッシュしたり、ブックマークしたり、ブラウザの履歴に残したり、長さに制約があったりすることがあります。ブックマークしたり、ブラウザの履歴に残したり、キャッシュしたりすることは、セキュリティ上の問題があるだけでなく、ユーザーが誤って同じ呼び出しを再度行ってしまうリスクも高くなります。このため、GETリクエストでデータを改変するのは得策ではありません。これを防ぐために、キャッシュせず、ブラウザの履歴にも残らない、ブックマークもできないPOSTリクエストを使うことができる。
ここで説明した理由により、createビューをPOSTリクエストに変更します。コードの直交性についてのコメントを思い出してください。ビューのルートがどのように処理されるかを定義するものは、ファクトリー内の views/to_do/mod.rs ファイルで、以下のコードで保持されています。
mod create;
use actix_web::web::{ServiceConfig, post, scope};
pub fn to_do_views_factory(app: &mut ServiceConfig) {
app.service(
scope("v1/item")
.route("create", post().to(create::create))
);
}
先のコードでは、インポートとルート定義で、getをpostに変更しただけであることがわかります。試しに先ほどの方法で新しいToDo項目を作成してみると、以下のような結果になります。
図4.5 ブロック化されたメソッド
図4.5では、ページが見つからないことが確認できます。このエラーは、ページが見つからないことを示す404-エラーであるため、混乱する可能性があります。URLはまだ定義されていますが、このURLではGETメソッドはもう許可されていません。これを踏まえて、以下のPostmanの設定を使用してPOSTコールを行うことができます。
図4.6 Postman postによるアイテム作成方法
図4.6では、POSTメソッドという異なるメソッドだけで、URLはまだ機能していることがわかります。状態ファイルを調べると、次のようなデータが見つかります。
createビューの許可メソッドを変更しても、ToDoアイテムの作成・保存方法には影響しないことがわかります。図4.6を振り返ってみると、ステータスコード200が返ってきていることも確認できます。これはすでに、作成が行われたことを物語っています。このため、ステータスがOKなので、何も返す必要はありません。
作成したビューにGETリクエストを送ろうとすると、not foundエラーが発生します。これが何を意味するのか、おわかりになりますか?同じURLを別のメソッドで再利用することを妨げるものはないのでしょうか?さて、ビューが見つからないのであれば、同じURLを別のメソッドで使うことを止めるものは何もないはずです。これは、views/to_do/mod.rsファイルの中で、次のコードで行うことができます。
app.service(
scope("v1/item")
.route("create/{title}", post().to(create::create))
.route("create/{title}", get().to(create::create))
);
ブラウザにURLを入力すると、保留中のToDo項目が作成され、これが動作することが確認できます。また、必要であれば、同じURLで別の関数をgetルートに入れることも可能です。このように、URLの使い方や再利用の仕方に柔軟性を持たせています。しかし、GETメソッドとPOSTメソッドの違いを考慮すると、Create関数にPOSTメソッドを使用することが賢明です。
これで、ToDoアイテムの作成に関しては、必要なことはすべて完了しました。しかし、他のビューでは、ToDoアイテムの現在の状態を示すために、構造化データを返す必要があります。
これまで、URLを使ってアプリケーションにデータを渡してきましたが、これはデータを渡すための最も基本的な方法です。しかし、構造化されたデータをURLで渡すことはできません。例えば、ハッシュマップやリストを送りたい場合、URLではそのような構造体を収容することができません。そこで、次のセクションで説明するJSONを使用して、リクエストのボディでアプリケーションにデータを渡す必要があります。