7.2 - ユーザーモデルを作成する
ユーザーモデルを作成する
このアプリではユーザーセッションを管理するため、ToDoアイテムの作成、削除、編集を許可する前に、ユーザーに関する情報を保存して、ユーザーの認証情報をチェックする必要があります。ここでは、ユーザーデータをPostgreSQLデータベースに保存することにします。これは必須ではありませんが、データベース内のユーザーをToDoアイテムにリンクさせる予定です。これにより、既存のテーブルを変更し、テーブル間のリンクを作成する方法について理解することができます。ユーザーモデルを作成するために、以下のことを行う必要があります。
- Userデータモデルを作成する。
- NewUserデータモデルを作成する。
- ToDo項目データ・モデルをユーザー・モデルにリンクできるように変更する。
- 新しいテーブルと変更したフィールドでスキーマファイルを更新する。
- データベース上で移行スクリプトを作成し、実行する。
次のセクションでは、前述のステップを詳しく見ていきます。
Userデータモジュールを作成する
開始する前に、Cargo.tomlファイルの依存関係を以下のように更新する必要があります。
パスワードのハッシュ化とチェックにはbcryptクレートを使用し、ユーザデータモデルの一意なIDの生成にはuuidクレートを使用する予定です。第6章PostgreSQLによるデータ永続化で説明したように、ユーザーデータモデル用に2つの異なる構造体を作成する必要があります。
新しいユーザーは、まだデータベースに存在しないため、idフィールドを持ちません。このIDは、新しいユーザーがテーブルに挿入されるときにデータベースによって作成されます。データベース内の既存のユーザーとやり取りするときにこのIDを使用する必要があるため、追加したidフィールドを含むすべての同じフィールドを持つ別の構造体を作成します。ID番号は、他のテーブルを参照する際に便利です。ID番号は短いし、一意であることが分かっているからです。ユーザーをToDo項目にリンクさせるために、ユーザーIDを使用する予定です。これらのデータモデルは、src/models.rsディレクトリにある以下のファイル構造に格納することができます。
new_user.rsファイルでデータモデルを定義します。まず、このようにインポートを定義する必要があります。
注意しなければならないのは、まだスキーマでユーザーを定義していないことです。これは、すべてのデータモデルを完成させた後に行うことにします。ユーザー・スキーマを定義するまでは、コードをコンパイルすることができません。また、新しいユーザーを作成するときにユニークなIDを作成するため、ユニークIDクレートをインポートし、新しいユーザーをデータベースに挿入するため、ディーゼルクレートからInsertable traitをインポートする予定です。次に、bcryptクレートからハッシュ関数を使用して、新しいユーザーのために定義した新しいパスワードをハッシュ化します。また、bcryptクレートからDEFAULT_COST定数をインポートしていることも確認できます。DEFAULT_COSTは、単にハッシュ関数に渡す定数です。なぜそうなるのかは、次章のパスワードのハッシュ化で説明します。これで、ユーザー データ モデル モジュールを定義し、必要なものをインポートしたので、次のセクションに進んで NewUser 構造体を作成することができます。
NewUserデータモデルを作成する
次のコードでデータモデルを定義することができます。
#[derive(Insertable, Clone)]
#[table_name="users"]
pub struct NewUser {
pub username: String,
pub email: String,
pub password: String,
pub unique_id: String,
}
ここで、データモデルが挿入可能であることを確認できます。しかし、クエリを実行することは許可していません。ユーザがデータベースから検索されたときに、そのユーザのIDが存在することを確認したいのです。ユーザーの一般的なデータ・モデルの定義に移ることもできますが、これは安全ではありません。パスワードをハッシュ化することで、確実に保護する必要があります。第2章「RustでWebアプリケーションを設計する」を覚えている方は、特定のToDo構造体にアクションを実行させるためにtraitを利用しました。ある構造体は作成でき、ある構造体は実装したtraitに基づいて削除することができました。ここでは、Insertable traitを実装するだけで、NewUser構造体の機能をロックダウンしています。しかし、次の図に示すように、User構造体に対して他のtraitを実装することで、クエリを実行できるようにします。
図7.1 「traitでデータモデルの構造体をロックダウンさせる
新しいユーザーをデータベースに挿入する構造体を作成したので、ユーザーのパスワードをデータベースに保存する方法を検討します。
なぜ、忘れたパスワードを復元できないのか、リセットしかできないのか、不思議に思ったことがあるかもしれません。これは、パスワードがハッシュ化されているためです。パスワードをハッシュ化することは、パスワードを保存する際によく行われることです。これは、アルゴリズムを使ってパスワードを難読化し、読み取ることができないようにするものです。これが行われると、元に戻すことはできません。
そして、ハッシュ化されたパスワードは、データベースに保存される。パスワードを確認するために、入力されたパスワードをハッシュ化し、データベース内のハッシュ化されたパスワードと比較します。これにより、入力されたハッシュ化されたパスワードが、データベースに保存されているハッシュ化されたパスワードと一致するかどうかを確認することができます。これには、いくつかの利点があります。まず、データにアクセスできる従業員にパスワードを知られることを防ぐことができます。また、データ漏洩があった場合、漏洩したデータから、そのデータを持っていた人にあなたのパスワードが直接知られるのを防ぐことができます。
多くの人が同じパスワードを複数のことに使っている(使ってはいけないのに)ことを考えると、パスワードをハッシュ化していないためにデータ漏洩が起きた場合、あなたのアプリを使っている人たちに与えるダメージは想像に難くないでしょう。しかし、ハッシュ化はこれよりもっと複雑です。同じパスワードをハッシュ化しても、同じハッシュにはならないようにする、ソルティングという概念があるのです。これは、ハッシュ化する前にパスワードに余分なビットのデータを追加することで実現されます。ハッシュ関数に渡すDEFAULT_COST定数も、このために用意されたものです。例えば、データベースのデータを手に入れ、その中にあるパスワードを推測するコードを書きたいとします。十分な計算能力があれば、効率よくパスワードを推測することができる。そこで、コスト・パラメータを渡すことができる。コスト・パラメータを増やすと、CPU時間かメモリのどちらかの作業量が指数関数的に増加します。コスト係数を1つ増やすと、ハッシュを計算するのに必要な演算回数が1万回、あるいはそれ以上に増える。
パスワードのセキュリティについてより詳細に説明することは、本書の範囲を超えています。しかし、パスワードを保存する際には、パスワードのハッシュ化は常に必須であることを強調しておかなければならない。幸いなことに、主要な言語には、わずか数行のコードでパスワードのハッシュ化とチェックを可能にするさまざまなモジュールが存在します。ここでもRustは変わりません。
新しいユーザーをハッシュ化されたパスワードでデータベースに挿入できるようにするため、次の手順を実行します。
- まず、次のように定義されたNewUserコンストラクタで、入力されたパスワードがハッシュ化されるようにする必要があります。
impl NewUser {
pub fn new(username: String,
email: String, password: String) -> NewUser {
let hashed_password: String = hash(
password.as_str(), DEFAULT_COST
).unwrap();
let uuid = Uuid::new_v4().to_string();
return NewUser {
username,
email,
password: hashed_password,
unique_id: uuid
}
}
}
ここでは、bcrypt crateのハッシュ関数を使ってパスワードをハッシュ化し、DEFAULT_COST定数も渡しています。また、Uuidクレートを使用して一意のIDを作成し、これらの属性を持つNewUser構造体の新しいインスタンスを構築しています。今回のアプリでは、一意なIDは実際には必要ありません。しかし、複数のサーバーやデータベースの間で通信を行う場合、これらは便利なものになります。
- NewUserデータモデルを定義したので、次のコードでuser.rsファイルに一般ユーザーデータモデルを定義することができます。まず、以下のインポートを定義する必要があります。
extern crate bcrypt;
use diesel::{Queryable, Identifiable};
use bcrypt::verify;
use crate::schema::users;
ここでは、verify機能を使用していることと、一般ユーザーデータモデル構造体がクエリ可能で識別可能であることを許可していることがわかります。
- 前のステップで定義したインポートを使用して、User構造体を構築することができます。この構造体は、データベースへのクエリを実行するときにデータベースから読み込まれる構造体であることを忘れないでください。User構造体はNewUser構造体と同じテーブルを使用しますが、idフィールドがあり、挿入ではなくクエリで処理されるためです。User構造体を構築したら、次のようなコードになるはずです。
#[derive(Queryable, Clone, Identifiable)]
#[table_name="users"]
pub struct User {
pub id: i32,
pub username: String,
pub email: String,
pub password: String,
pub unique_id: String
}
idフィールドを追加しただけで、Insertable traitの代わりにQueryable traitを派生させていることがわかりますね。
- User構造体が定義されたので、次のコードで、入力されたパスワードがユーザーに属するパスワードと一致するかどうかを検証する関数を構築することができます。
impl User {
pub fn verify(&self, password: String) -> bool {
verify(password.as_str(),
&self.password).unwrap()
}
}
- さて、モデルが定義されたので、忘れずにmodels/user/mod.rsファイルに以下のコードでモデルを登録しなければなりません。
- さらに、models/mod.rsファイルに以下の行を追加することで、これらのモジュールにアプリからアクセスできるようにすることができます。
これで、ユーザーのデータモデルが定義されました。しかし、まだToDo項目とリンクさせる必要があります。
ToDoアイテムのデータモデルを変更する
データモデルをToDo項目にリンクさせるためには、ToDoデータモデルを変更する必要があります。これを行うには、複数の方法があります。例えば、アイテムテーブルにuser_idフィールドを追加し、userテーブルのunique_idフィールドをそのまま使うことができます。新しいアイテムを作成するときに、そのユーザーのユニークIDをアイテムのコンストラクタに渡します。これは簡単に実装できるのですが、しかし、リスクもあります。ユーザーのユニークIDをアイテムに渡すだけでは、そのユーザーのIDが有効であり、データベースにあることを強制するものではありません。削除されたユーザーのIDをアイテムコンストラクタに挿入して、孤児となったアイテムをデータベースに挿入することを止めるものは何もありません。この場合、孤児となったアイテムが関連するユーザーIDへの参照がないため、後で抽出するのは困難です。次の図のように、ユーザーのIDとアイテムのIDを参照する新しいテーブルを作成することもできます。
図7.2 「ユーザーとアイテムの関連付けを記録するための独立したデータベーステーブル
この利点は、テーブルを削除するだけで、ユーザーとアイテムを簡単に切り離すことができることです。しかし、新しいエントリーを作成する際に、有効なユーザーIDの強制やアイテムIDの強制を行うことはできません。また、ユーザーからアイテムを取得するために、アソシエーションテーブルとアイテムテーブルの2つのクエリーを作成する必要があります。アイテム・テーブルにユーザーIDカラムを付加する方法と、アイテムIDとユーザー固有IDを保持するブリッジ・テーブルを作成する方法の2つは簡単に実装できるので、ここでは説明せず、この時点で自分で実装できるようにします。ToDoアプリケーションの文脈では、先の2つの方法は、何のメリットもなく、データベースにデータを挿入する際にエラーが発生する危険性があるため、劣った方法となります。とはいえ、先の2つの方法を絶対に使ってはいけないというわけではありません。プロジェクトによって、必要なデータは異なります。私たちのプロジェクトでは、次の図に示すように、ユーザーとアイテムを結びつける外部キーを作成する予定です。
図7.3 - ユーザーとアイテム間の外部キー関連付け
このため、1回のデータベース呼び出しでユーザーに関連するアイテムにアクセスすることはできませんが、データベース内の正規のユーザーIDへの参照を持つアイテムのみを挿入することができます。外部キーはまた、カスケードイベントを引き起こすことができ、ユーザーを削除すると、そのユーザーに関連する既存のアイテムがすべて自動的に削除され、孤児が作成されるのを防ぐことができます。外部キーは、マクロでテーブルへのリンクを宣言することで作成します。models/item/item.rsでは、最初に以下のインポートを用意することでこれを実現します。
次のコードに示すように、Item構造体がUser構造体に属していることを主張するために、belongs_toマクロで参照することになるので、User構造体をインポートする必要があることがわかります。
#[derive(Queryable, Identifiable, Associations)]
#[belongs_to(User)]
#[table_name="to_do"]
pub struct Item {
pub id: i32,
pub title: String,
pub status: String,
pub date: NaiveDateTime,
pub user_id: i32,
}
ここでは、ユーザーデータモデル構造体をインポートし、belongs_toマクロで定義し、user_idフィールドを追加して構造体をリンクしていることが確認できます。なお、Associationsマクロを含めないと、belongs_toマクロは呼び出すことができません。
最後に、models/item/new_item.rsファイルのfieldsとconstructorにuser_idフィールドを追加する必要があります。これは、新しいToDo項目を、その項目を作成したユーザーにリンクさせるために必要なことです。これは、次のコードを使用することで実現できます。
use crate::schema::to_do;
use chrono::{NaiveDateTime, Utc};
#[derive(Insertable)]
#[table_name="to_do"]
pub struct NewItem {
pub title: String,
pub status: String,
pub date: NaiveDateTime,
pub user_id: i32,
}
impl NewItem {
pub fn new(title: String, user_id: i32) -> NewItem {
let now = Utc::now().naive_local();
NewItem{
title, status: String::from("PENDING"),
date: now,
user_id
}
}
}
そこで、これまでの内容を整理すると、データモデル構造体はすべて変更され、アプリでデータベースとやり取りする際に必要なときに必要な分だけ使用できるようになりました。しかし、データベースを更新していませんし、アプリとデータベースをつなぐブリッジも更新していません。次にこれを行う予定です。
スキーマファイルの更新
データモデル構造体からデータベースへのマッピングが最新であることを確認するために、これらの変更に合わせてスキーマを更新する必要があります。つまり、ToDo項目テーブルの既存のスキーマを変更し、src/schema.rsファイルにユーザースキーマを追加する必要があります。これは、次のコードで示されます。
table! {
to_do (id) {
id -> Int4,
title -> Varchar,
status -> Varchar,
date -> Timestamp,
user_id -> Int4,
}
}
table! {
users (id) {
id -> Int4,
username -> Varchar,
email -> Varchar,
password -> Varchar,
unique_id -> Varchar,
}
}
スキーマファイルのフィールドは、Rustのデータモデルと同じ順序で定義されていることに注意する必要があります。これは重要なことで、そうしないと、データベースに接続するときにフィールドが不一致になるからです。また、スキーマは単にフィールドとその型を定義しているだけで、ToDoテーブルとユーザーテーブルの関係を網羅しているわけではないこともお分かりいただけると思います。
なぜなら、私たちが独自のマイグレーションを作成し実行すると、このスキーマファイルはリレーションシップで更新されるからです。このため、このスキーマファイルを完成させるために、独自のマイグレーションを作成することになります。
データベース上での移行スクリプトの作成と実行
マイグレーションの実行は、第6章「PostgreSQLによるデータ永続化」で取り上げた、Dieselクライアントのインストールとデータベースへの接続方法と同様の流れになります。まず、docker-composeコマンドでデータベースを実行する必要があります。
マイグレーションを実行する際には、これをバックグラウンドで実行する必要があります。次に、以下のコマンドを実行することで、マイグレーションスクリプトを作成することができます。
これにより、migrationsの中にディレクトリが作成され、そのディレクトリのユーザー名にcreate_usersが含まれる。このディレクトリの中に、2つの空のSQLファイルがあります。ここでは、migrationsのための独自のSQLスクリプトを手動で記述します。当初は、他の言語でもこれらのマイグレーションを自動生成するライブラリがあるので、不要だと思うかもしれませんが、これを行うことにはいくつかの利点があります。
は、まず、もう一つの便利なツールであるSQLに手をつけておくことができます。これにより、私たちが解決しようとしている日々の問題で、SQLを活用したソリューションを考えることができるようになります。また、マイグレーションの流れをより細かくコントロールすることができます。たとえば、これから作成するマイグレーションでは、ユーザーテーブルとベースユーザーを作成し、to_doテーブルのカラムを変更する際に、プレースホルダーユーザー行のIDで埋めることができるようにする必要があります。これをup.sqlファイルで次のようなテーブル定義で実行します。
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR NOT NULL UNIQUE,
email VARCHAR NOT NULL UNIQUE,
password VARCHAR NOT NULL,
unique_id VARCHAR NOT NULL
);
これは分かりやすいですね。emailとusernameのフィールドは一意であることに注意してください。これは、ユーザー名と電子メールが重複しているユーザーを排除するためです。このレベルで制約を入れるのは、いくつかの理由からです。たとえば、ユーザー名と電子メールをデータベースから呼び出し、重複している場合は新しいユーザーの挿入を拒否することで、この問題から保護することができます。
しかし、コードに誤りがあるかもしれませんし、将来誰かが私たちのコードを変更するかもしれません。編集機能など、このチェックがない新機能が導入されるかもしれない。行を変更したり、新しいユーザーを挿入したりする移行があるかもしれません。自分でSQLを書く場合は、操作が終了したことを示すために;記号を使用することを確認することが、通常、ベストプラクティスです。
このSQLコマンドが発行され、その後に次のコマンドが発行されます。up.sqlファイルの次のコマンドは、次のコマンドでプレースホルダ・ユーザ行を挿入します。
INSERT INTO users (username, email, password, unique_id)
VALUES ('placeholder', 'placeholder email', 'placeholder password', 'placeholder unique id');
ユーザーを作成したので、次にto_doテーブルを変更します。これは、先ほどのコマンドと同じファイル内で、次のコマンドを実行することで可能です。
以上で、up.sqlマイグレーションは定義されました。次に、down.sqlマイグレーションを定義する必要があります。downマイグレーションでは、基本的にupマイグレーションで行ったことを逆にしなければなりません。つまり、to_doテーブルのuser_idカラムを削除し、さらにuserテーブルを完全に削除します。これは、down.sqlファイル内の以下のSQLコードで行うことができます。
マイグレーションがデータベースに影響を与えるためには、Dockerが起動している必要があることを念頭に置いておく必要があります。このマイグレーションが実行されると、src/schema.rsファイルに以下のコードが追加されていることが確認できます。
これにより、RustデータモデルがユーザーとToDoアイテムの関係に関するクエリーを実行できるようになります。このマイグレーションが完了したら、アプリを再び実行することができます。しかし、その前に、src/views/to_do/create.rs ファイルを少し変更する必要があります。create ビュー関数の新しいアイテムのコンストラクタで、次の行のコードを使用してデフォルトのユーザー ID を追加します。
今アプリを実行すると、第6章PostgreSQLによるデータ永続化で説明したのと同じ動作になり、作成したマイグレーションでアプリが実行されます。しかし、パスワードをハッシュ化し、一意のIDを生成する際に、新しいユーザーのコンストラクタが機能するかどうかも確認する必要があります。
これを行うには、ユーザー作成エンドポイントを構築する必要があります。そのためには、スキーマを定義し、新しいユーザーをデータベースに挿入するビューを定義する必要があります。スキーマは、src/json_serialization/new_user.rs ファイルに以下のコードで作成できます。
use serde::Deserialize;
#[derive(Deserialize)]
pub struct NewUserSchema {
pub name: String,
pub email: String,
pub password: String
}
この後、src/json_serialization/mod.rs ファイルで pub mod new_user; として新しいユーザースキーマを宣言することができます。スキーマが定義されたら、次のようなファイル構成で独自のユーザ・ビュー・モジュールを作成することができます。
users/create.rsファイルでは、createビュー関数を構築する必要があります。まず、以下のクレートをインポートします。
use crate::diesel;
use diesel::prelude::*;
use actix_web::{web, HttpResponse, Responder};
use actix_web::HttpResponseBuilder;
use crate::database::DB;
use crate::json_serialization::new_user::NewUserSchema;
use crate::models::user::new_user::NewUser;
use crate::schema::users;
これまで何度もビューを構築してきたので、これらのインポートは驚くようなものではありません。データベースへの呼び出しを可能にするために、Dieselマクロとcrateをインポートします。次に、actix_web traitsとstructsをインポートして、ビューへのデータの流入と流出を可能にします。次に、スキーマと構造体をインポートして、受信および処理するデータを構造化します。正しいクレートをインポートしたので、次のコードでビュー作成関数を定義する必要があります。
ここでは、NewUserSchema構造体に読み込まれたJSONデータを受け入れていることがわかります。また、DB構造体を使用して、接続プールからデータベース接続を確立しています。create view関数内では、NewUserSchema構造体から必要なデータを抽出し、以下のコードでNewUser構造体を作成しています。
let new_user = NewUser::new(
new_user.name.clone(),
new_user.email.clone(),
new_user.password.clone()
);
NewUserコンストラクタに渡すフィールドのクローンを作成する必要があります。これは、文字列がCopy traitを実装していないためで、手動で行う必要があります。次に、データベースへの挿入コマンドを作成し、次のコードでそれを実行します。
これはResult構造体を返します。ただし、直接アンラップするわけではありません。コンフリクトが発生する可能性があるからです。たとえば、すでにデータベースにあるユーザー名や電子メールを持つ新しいユーザーを挿入しようとすることがあります。しかし、これがエラーになるのは避けたいところです。これは、ユニークなユーザー名と電子メールの制約を私たち自身が実装しているため、予想されるエッジケースです。もし、ビューの実行時に正当なエラーが発生したのであれば、私たちはそれを知る必要があります。したがって、エッジケースに応答コードを与える必要があります。そこで、次のコードでinsertの結果を照合し、適切なレスポンスコードを返します。
ここでは、データベース接続を確立し、JSON本体からフィールドを抽出して、新しいNewUser構造体を作成し、それをデータベースに挿入しています。ここでは、他のビューと比較して、若干の違いがあります。リターン・レスポンスでは、awaitしてからunwrapする必要があるのです。これは、JSONボディを返さないためです。したがって、HttpResponse::Ok()は、単なるビルダー構造体である。
さて、create viewを構築したので、views/users/mod.rsファイルにビューファクトリーを次のように定義する必要があります。
mod create;
use actix_web::web::{ServiceConfig, post, scope};
pub fn user_views_factory(app: &mut ServiceConfig) {
app.service(
scope("v1/user")
.route("create", post().to(create::create))
);
}
繰り返しになりますが、私たちは定期的にビューを構築しているので、このどれもが驚くようなことではありません。もしそうなら、第3章HTTPリクエストの処理のActix Webフレームワークを使用したビューの管理セクションを読んで、明確にすることをお勧めします。さて、views/mod.rsファイル内のメインビューファクトリーは、以下のような感じになっているはずです。
mod auth;
mod to_do;
mod app;
mod users;
use auth::auth_views_factory;
use to_do::to_do_views_factory;
use app::app_views_factory;
use users::user_views_factory;
use actix_web::web::ServiceConfig;
pub fn views_factory(app: &mut ServiceConfig) {
auth_views_factory(app);
to_do_views_factory(app);
app_views_factory(app);
user_views_factory(app);
}
ユーザービューの登録が完了したので、アプリを実行し、次のPostmanコールでユーザーを作成します。
図7.4 - Postmanによるcreate userエンドポイントへの呼び出し
これで、201のcreated responseを得ることができるはずです。まったく同じ呼び出しをもう一度行うと、409コンフリクトが返されるはずです。これで、新しいユーザが作成されたことになります。第6章PostgreSQLによるデータ永続化」の「DieselによるPostgreSQLへの接続」で説明した手順で、Dockerコンテナ内のデータベースを検査すると、次のように表示されます。
```rust id | name | email ----+-------------+------------------- 1 | placeholder | placeholder email 2 | maxwell | test@gmail.com password
placeholder password $2b$12$jlfLwu4AHjrvTpZrB311Y.W0JulQ71WVy2g771xl50e5nS1UfqwQ. unique_id
placeholder unique id 543b7aa8-e563-43e0-8f62-55211960a604 ```
ここでは、マイグレーションで作成された初期ユーザーを見ることができます。しかし、ビューで作成したユーザーも見ることができます。ここでは、ハッシュ化されたパスワードと一意のIDがあります。NewUser構造体に属するコンストラクタ関数を通じてのみ、ユーザーを作成する必要があります。
私たちのアプリの文脈では、ユニークなIDは必要ありません。しかし、複数のサーバーやデータベースを使用するような状況では、ユニークなIDが役に立つことがあります。また、2回目の競合対応は正しく、3回目のレプリカ作成ユーザー呼び出しでは、データベースにレプリカユーザーが挿入されなかったことにも注意しなければなりません。
これで、ToDo項目にリンクされたユーザーモデルを持つユーザーテーブルが存在することになり、私たちのアプリは通常通り稼働するようになりました。このように、リレーションシップと構造マイグレーションを持つ他のデータテーブルを作成することで、シームレスにアップグレードやダウングレードができるようになります。また、パスワードの検証や作成方法についても解説しました。しかし、実際にユーザーが正しい認証情報を渡しているかどうかをチェックするコードは書いていません。次のセクションでは、ユーザーを認証し、正しい資格情報が含まれていないリクエストを拒否することに取り組みます。