バリデーション
AdonisJSでのデータバリデーションは通常、コントローラーレベルで行われます。これにより、アプリケーションがリクエストを処理し、フォームフィールドの横に表示できるエラーをレスポンスで送信する前に、ユーザー入力をバリデーションできるようになります。
バリデーションが完了したら、信頼できるデータを使用してデータベースクエリ、スケジュールされたキュージョブ、メールの送信など、他の操作を実行できます。
バリデーションライブラリの選択
AdonisJSのコアチームは、フレームワークに依存しないデータバリデーションライブラリであるVineJSを作成しました。VineJSを使用する理由のいくつかは以下の通りです。
-
Node.jsエコシステムで最速のバリデーションライブラリの1つです。
-
実行時のバリデーションとともに静的な型安全性を提供します。
-
web
およびapi
のスターターキットには事前に構成されています。 -
公式のAdonisJSパッケージは、VineJSにカスタムルール(例:Lucidは
unique
とexists
ルールを提供)を追加します。
ただし、AdonisJSは厳密にVineJSの使用を強制しません。自分やチームに最適なバリデーションライブラリを使用できます。単に@vinejs/vine
パッケージをアンインストールし、使用したいパッケージをインストールしてください。
VineJSの設定
次のコマンドを使用してVineJSをインストールおよび設定します。
参照も: VineJSのドキュメント
node ace add vinejs
-
検出されたパッケージマネージャーを使用して
@vinejs/vine
パッケージをインストールします。 -
adonisrc.ts
ファイル内に以下のサービスプロバイダーを登録します。{providers: [// ...他のプロバイダー() => import('@adonisjs/core/providers/vinejs_provider')]}
バリデータの使用
VineJSでは、バリデータのコンセプトを使用します。アプリケーションが実行できる各アクションごとにバリデータを作成します。たとえば、新しい投稿の作成のためのバリデータ、投稿の更新のための別のバリデータ、および投稿の削除のためのバリデータを定義します。
ブログを例にして、投稿の作成/更新のためのバリデータを定義します。まず、いくつかのルートとPostsController
を登録しましょう。
import router from '@adonisjs/core/services/router'
const PostsController = () => import('#controllers/posts_controller')
router.post('posts', [PostsController, 'store'])
router.put('posts/:id', [PostsController, 'update'])
node ace make:controller post store update
import { HttpContext } from '@adonisjs/core/http'
export default class PostsController {
async store({}: HttpContext) {}
async update({}: HttpContext) {}
}
バリデータの作成
PostsController
を作成し、ルートを定義したら、次のaceコマンドを使用してバリデータを作成できます。
参照も: バリデータの作成コマンド
node ace make:validator post
バリデータはapp/validators
ディレクトリ内に作成されます。バリデータファイルはデフォルトでは空であり、複数のバリデータをエクスポートするために使用できます。各バリデータは、vine.compile
メソッドの結果を保持するconst
変数です。
次の例では、createPostValidator
とupdatePostValidator
を定義しています。両方のバリデータはスキーマにわずかな違いがあります。作成時には、ユーザーが投稿のためにカスタムスラッグを提供できるようにしますが、更新時には許可しません。
バリデータスキーマ内の重複についてはあまり心配しないでください。重複を避けるためにすべてのコストを払う代わりに、理解しやすいスキーマを選択することをオススメします。WETコードベースの類推は、重複を受け入れるのに役立つかもしれません。
import vine from '@vinejs/vine'
/**
* 投稿の作成アクションをバリデーションします
*/
export const createPostValidator = vine.compile(
vine.object({
title: vine.string().trim().minLength(6),
slug: vine.string().trim(),
description: vine.string().trim().escape()
})
)
/**
* 投稿の更新アクションをバリデーションします
*/
export const updatePostValidator = vine.compile(
vine.object({
title: vine.string().trim().minLength(6),
description: vine.string().trim().escape()
})
)
コントローラー内でバリデータを使用する
PostsController
に戻り、バリデータを使用してリクエストボディをバリデーションします。request.all()
メソッドを使用してリクエストボディにアクセスできます。
import { HttpContext } from '@adonisjs/core/http'
import {
createPostValidator,
updatePostValidator
} from '#validators/post_validator'
export default class PostsController {
async store({ request }: HttpContext) {
const data = request.all()
const payload = await createPostValidator.validate(data)
return payload
}
async update({ request }: HttpContext) {
const data = request.all()
const payload = await updatePostValidator.validate(data)
return payload
}
}
以上です!ユーザー入力のバリデーションは、コントローラー内の2行のコードです。バリデーションされた出力には、スキーマから推論された静的な型情報が含まれています。
また、validate
メソッド呼び出しをtry/catch
でラップする必要はありません。エラーの場合、AdonisJSは自動的にエラーをHTTPレスポンスに変換します。
エラーハンドリング
HttpExceptionHandlerは、バリデーションエラーを自動的にHTTPレスポンスに変換します。例外ハンドラはコンテンツネゴシエーションを使用し、Acceptヘッダーの値に基づいてレスポンスを返します。
ExceptionHandlerのコードベースをのぞいて、バリデーション例外がHTTPレスポンスに変換される方法を確認してください。
また、セッションミドルウェアは、renderValidationErrorAsHTMLメソッドを上書きし、フラッシュメッセージを使用してフォームとバリデーションエラーを共有します。
-
Accept=application/json
ヘッダーを持つHTTPリクエストは、SimpleErrorReporterを使用して作成されたエラーメッセージの配列を受け取ります。 -
Accept=application/vnd.api+json
ヘッダーを持つHTTPリクエストは、JSON API仕様にしたがってフォーマットされたエラーメッセージの配列を受け取ります。 -
セッションパッケージを使用したサーバーレンダリングされたフォームは、セッションフラッシュメッセージを介してエラーを受け取ります。
-
その他のリクエストは、エラーをプレーンテキストで受け取ります。
request.validateUsingメソッド
コントローラー内でバリデーションを実行する推奨される方法は、request.validateUsing
メソッドを使用することです。request.validateUsing
メソッドを使用する場合、バリデーションデータを明示的に定義する必要はありません。リクエストボディ、クエリ文字列の値、ファイルがマージされ、データがバリデータに渡されます。
import { HttpContext } from '@adonisjs/core/http'
import {
createPostValidator,
updatePostValidator
} from '#validators/posts_validator'
export default class PostsController {
async store({ request }: HttpContext) {
const data = request.all()
const payload = await createPostValidator.validate(data)
const payload = await request.validateUsing(createPostValidator)
}
async update({ request }: HttpContext) {
const data = request.all()
const payload = await updatePostValidator.validate(data)
const payload = await request.validateUsing(updatePostValidator)
}
}
クッキー、ヘッダー、ルートパラメータのバリデーション
request.validateUsing
メソッドを使用する場合、クッキー、ヘッダー、ルートパラメータを次のようにバリデーションできます。
const validator = vine.compile(
vine.object({
// リクエストボディのフィールド
username: vine.string(),
password: vine.string(),
// クッキーのバリデーション
cookies: vine.object({
}),
// ヘッダーのバリデーション
headers: vine.object({
}),
// ルートパラメータのバリデーション
params: vine.object({
}),
})
)
await request.validateUsing(validator)
バリデータへのメタデータの渡し方
バリデータはリクエストライフサイクルの外部で定義されているため、バリデータはリクエストデータに直接アクセスできません。これは通常良いことですが、ランタイムデータにアクセスする必要がある場合は、validate
メソッド呼び出し中にメタデータとして渡す必要があります。
ただし、バリデータがランタイムデータにアクセスする必要がある場合は、validate
メソッド呼び出し中にメタデータとして渡す必要があります。
unique
バリデーションルールの例を見てみましょう。データベース内でユーザーのメールアドレスが一意であることを確認したいが、現在ログインしているユーザーの行はスキップしたい場合です。
export const updateUserValidator = vine
.compile(
vine.object({
email: vine.string().unique(async (db, value, field) => {
const user = await db
.from('users')
.whereNot('id', field.meta.userId)
.where('email', value)
.first()
return !user
})
})
)
上記の例では、meta.userId
プロパティを介して現在ログインしているユーザーにアクセスしています。HTTPリクエストでuserId
を渡す方法を見てみましょう。
async update({ request, auth }: HttpContext) {
await request.validateUsing(
updateUserValidator,
{
meta: {
userId: auth.user!.id
}
}
)
}
メタデータの型安全性
前の例では、バリデーション時にmeta.userId
を渡すことを覚えておく必要があります。TypeScriptにそれを思い出させてもらえると素晴らしいです。
次の例では、vine.withMetaData
関数を使用して、スキーマで使用するメタデータの静的な型を定義しています。
export const updateUserValidator = vine
.withMetaData<{ userId: number }>()
.compile(
vine.object({
email: vine.string().unique(async (db, value, field) => {
const user = await db
.from('users')
.whereNot('id', field.meta.userId)
.where('email', value)
.first()
return !user
})
})
)
注意してください、VineJSはランタイムでメタデータをバリデーションしません。ただし、それを行いたい場合は、withMetaData
メソッドにコールバックを渡し、手動でバリデーションを行うことができます。
vine.withMetaData<{ userId: number }>((meta) => {
// メタデータをバリデーションする
})
VineJSの設定
start
ディレクトリ内にプリロードファイルを作成して、VineJSをカスタムエラーメッセージで設定するか、カスタムエラーレポーターを使用できます。
node ace make:preload validator
次の例では、カスタムエラーメッセージを定義しています。
import vine, { SimpleMessagesProvider } from '@vinejs/vine'
vine.messagesProvider = new SimpleMessagesProvider({
// すべてのフィールドに適用される
'required': '{{ field }}フィールドは必須です',
// すべてのフィールドに適用される
'string': '{{ field }}フィールドの値は文字列である必要があります',
// すべてのフィールドに適用される
'email': '有効なメールアドレスではありません',
// usernameフィールドのエラーメッセージ
'username.required': 'アカウントのユーザー名を選択してください',
})
以下の例では、カスタムエラーレポーターを登録しています。
import vine, { SimpleMessagesProvider } from '@vinejs/vine'
import { JSONAPIErrorReporter } from '../app/validation_reporters.js'
vine.errorReporter = () => new JSONAPIErrorReporter()
AdonisJSによって提供されるルール
以下は、AdonisJSによって提供されるVineJSルールのリストです。
- AdonisJSのコアパッケージによって追加された
vine.file
スキーマタイプ。