こちらは、現時点で最新のAdonisJS v5の記事になります。
Validatorの導入手順とAdonis v4との違いについて紹介します。
Validatorとは、リクエストで送られてきたデータを解析・検証して異常があれば、エラーメッセージをレスポンスすることができる機能です。
公式リファレンス
https://docs.adonisjs.com/guides/validator/introduction
Adonis v4ではRouteにvalidatorというメソッドがありましたが、v5からはController内で行います。
Adonis v4
Route.post('users', 'UserController.store').validator('User/CreateUser')
Adonis v5
export default class UsersController {
public async store({ auth, request, response }: HttpContextContract) {
const payload = await request.validate(StoreUserValidator)
const user = await User.create(payload)
...
ValidatorClassを作っていく
リクエストで送られてくるデータの検証をするスキーマ、エラーの場合のエラーメッセージを定義するClassを作ります。
例に上記で書いたStoreUserValidatorを作っていきます。
$ node ace make:validator User/StoreUser
CREATE: app/Validators/User/StoreUserValidator.ts
import { schema, CustomMessages } from '@ioc:Adonis/Core/Validator'
import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class StoreUserValidator {
constructor(protected ctx: HttpContextContract) {}
public schema = schema.create({})
public messages: CustomMessages = {}
}
ValidatorClassが生成されたので、schemaとmessagesを編集していく。
Validator Schemaの書き方
schemaでは、リクエストで送られてくるデータを検証するためのValidationRuleを決めます。
リクエストで、メールアドレスとパスワードを送って来てほしい場合は以下のようになります。
export default class StoreUserValidator {
public schema = schema.create({
email: schema.string([
rules.required(),
rules.email(),
rules.unique({ table: 'users', column: 'email' })
]),
password: schema.string([rules.required()]),
})
}
emailは、string型で必須required
, メール形式email
, DB上のusersテーブルで重複がないかunique
をルールにしています。
passwordは、string型で必須required
をルールにしています。
他にも様々なルールが用意されているので、以下のリンクの左メニューから探しください。
https://docs.adonisjs.com/reference/validator/rules/alpha
Validationメッセージの書き方
続いてはルールに違反したときのメッセージについてですが、これは定義しなくてもデフォルトで設定されているのでエラーがでない心配は無いのですが、英語です。。。
ですので、日本語で返せるようにだったり、「メールアドレスは必須です」や「パスワードを入力してください」のように同じrequiredルールでも表示を変えたい場合はカスタムメッセージを定義する必要があります。
簡単に書くと以下のようになります。
export default class StoreUserValidator {
...
public messages: CustomMessages = {
required: 'The {{ field }} is required to create a new account',
'email.required': 'メールアドレスは必須です',
'password.required': 'パスワードを入力してください',
}
}
これでも動作はするのですが、StoreUserValidatorだったりLoginUserValidator, UpdateUserValidatorだったり、Validatorが増えたときにそれぞれで同じメッセージを書く必要があり、メール, メールアドレス, Emailのような表記のゆれや、変更があったときに修正漏れの可能性が高くなります。
なので、メッセージを共通化をしていきます。
Validatorメッセージの共通化
やり方としては、
- 各ルールのエラーメッセージを一つのファイルにまとめる。
- email, passwordといった項目がメールアドレス、パスワードといった表示名を一つのファイルにまとめる
- カスタムメッセージでそこから参照できるように設定する
これを実現するために少しトリッキーかもしれませんが多言語サポートで使われるi18nを使おうと思います。
@adonisjs/i18nを導入。
$ yarn add @adonisjs/i18n
$ node ace configure @adonisjs/i18n
Server.middleware.register([
() => import('App/Middleware/DetectUserLocale')
])
日本語しか使わないから、設定を日本語のみに変更
defaultLocale: 'ja',
supportedLocales: ['ja'],
翻訳データはjsonでもyamlでも書ける。
yaml推しなのでyamlで書いていく。
i18nはValidatorMessageに対応しています。
https://docs.adonisjs.com/guides/i18n#validator-messages
validator.yamlのsharedに書くと自動的にValidationMessageに紐づきます。
shared:
maxLength: "最大{maxLength}文字までです"
required: "{field}は必須です"
email: "{field}が正しくありません"
unique: "{field}は既に存在しています"
ただこのままだと、fieldが翻訳されずにemailが正しくありません
のようにemailと表示されてしまうので、メールアドレスが正しくありません
と日本語名で表示できるように修正していきます。
fieldの翻訳データを用意する。
email: 'メールアドレス'
password: 'パスワード'
Validatorをカスタマイズする。
export default class StoreUserValidator {
// 追加
get fields() {
return {
email: this.ctx.i18n.formatMessage('user.email'),
password: this.ctx.i18n.formatMessage('user.password'),
}
}
public messages: CustomMessages = {
'*': (field, rule, arrayExpressionPointer, options) => {
try {
return this.ctx.i18n.formatMessage(`validator.shared.${rule}`, { field: this.fields[field] })
} catch (_) {
return this.ctx.i18n.validatorMessages()['*'](field, rule, arrayExpressionPointer, options)
}
},
}
}
i18n.formatMessage(`validator.shared.${rule}`, { field: this.fields[field] })
が、validator.yamlで定義したValidationメッセージに翻訳されたfieldを渡して、fieldも翻訳されたエラーメッセージを出力している。
これでemailフィールドはresouces/locale/*/user.yamlから翻訳データを取得してValidatorメッセージのfieldに出力でき、メールアドレスが正しくありません
と表示される。
もしfieldが定義されていなかったりのエラーの可能性も考慮してtry-catchで通常のi18n.validatorMessages()
に渡している。
以上にて、AdonisでValidator設定が完了。