Validação
A validação de dados no AdonisJS geralmente é realizada no nível do controlador. Isso garante que você valide a entrada do usuário assim que seu aplicativo manipular a solicitação e enviar erros na resposta que podem ser exibidos ao lado dos campos do formulário.
Uma vez que a validação for concluída, você pode usar os dados confiáveis para executar o restante das operações, como consultas ao banco de dados, agendamento de trabalhos de fila, envio de e-mails, etc.
Escolhendo a biblioteca de validação
A equipe principal do AdonisJS criou uma biblioteca de validação de dados agnóstica de estrutura chamada VineJS. A seguir estão alguns dos motivos para usar o VineJS.
É uma das bibliotecas de validação mais rápidas no ecossistema Node.js.
Fornece segurança de tipo estático junto com as validações de tempo de execução.
Ele vem pré-configurado com os kits iniciais
web
eapi
.Os pacotes oficiais do AdonisJS estendem o VineJS com regras personalizadas. Por exemplo, o Lucid contribui com as regras
unique
eexists
para o VineJS.
No entanto, o AdonisJS não força você tecnicamente a usar o VineJS. Você pode usar qualquer biblioteca de validação que seja ótima para você ou sua equipe. Basta desinstalar o pacote @vinejs/vine
e instalar o pacote que deseja usar.
Configurando o VineJS
Instale e configure o VineJS usando o seguinte comando.
node ace add vinejs
Veja as etapas executadas pelo comando add
Instala o pacote
@vinejs/vine
usando o gerenciador de pacotes detectado.Registra o seguinte provedor de serviços dentro do arquivo
adonisrc.ts
.ts{ providers: [ // ...outros provedores () => import('@adonisjs/core/providers/vinejs_provider') ] }
Usando validadores
O VineJS usa o conceito de validadores. Você cria um validador para cada ação que seu aplicativo pode executar. Por exemplo: defina um validador para criar uma nova postagem, outro para atualizar a postagem e talvez um validador para excluir uma postagem.
Usaremos um blog como exemplo e definiremos validadores para criar/atualizar uma postagem. Vamos começar registrando algumas rotas e o PostsController
.
// Define as rotas
import router from '@adonisjs/core/services/router'
const PostsController = () => import('#controllers/posts_controller')
router.post('posts', [PostsController, 'store'])
router.put('posts/:id', [PostsController, 'update'])
# Crie o controlador
node ace make:controller post store update
// Scaffold controller
import { HttpContext } from '@adonisjs/core/http'
export default class PostsController {
async store({}: HttpContext) {}
async update({}: HttpContext) {}
}
Criando validadores
Depois de criar o PostsController
e definir as rotas, você pode usar o seguinte comando ace para criar um validador.
Veja também: Comando Make validator
node ace make:validator post
Os validadores são criados dentro do diretório app/validators
. O arquivo do validador está vazio por padrão, e você pode usá-lo para exportar vários validadores dele. Cada validador é uma variável const
que contém o resultado do método vine.compile
.
No exemplo a seguir, definimos createPostValidator
e updatePostValidator
. Ambos os validadores têm uma pequena variação em seus esquemas. Durante a criação, permitimos que o usuário forneça um slug personalizado para a postagem, enquanto não permitimos sua atualização.
NOTA
Não se preocupe muito com a duplicação dentro dos esquemas do validador. Recomendamos que você opte por esquemas fáceis de entender em vez de evitar duplicação a todo custo. A analogia da base de código molhada pode ajudá-lo a adotar a duplicação.
// app/validators/post_validator.ts
import vine from '@vinejs/vine'
/**
* Valida a ação de criação da postagem
*/
export const createPostValidator = vine.compile(
vine.object({
vine.string().trim().minLength(6),
slug: vine.string().trim(),
description: vine.string().trim().escape()
})
)
/**
* Valida a ação de atualização da postagem
*/
export const updatePostValidator = vine.compile(
vine.object({
vine.string().trim().minLength(6),
description: vine.string().trim().escape()
})
)
Usando validadores dentro de controladores
Vamos voltar ao PostsController
e usar os validadores para validar o corpo da solicitação. Você pode acessar o corpo da solicitação usando o método 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
}
}
Isso é tudo! Validar a entrada do usuário são duas linhas de código dentro dos seus controladores. A saída validada tem informações de tipo estático inferidas do esquema.
Além disso, você não precisa encapsular a chamada do método validate
dentro de um try/catch
. Porque no caso de um erro, o AdonisJS converterá automaticamente o erro em uma resposta HTTP.
Tratamento de erros
O HttpExceptionHandler converterá os erros de validação em uma resposta HTTP automaticamente. O manipulador de exceções usa negociação de conteúdo e retorna uma resposta com base no valor do cabeçalho Accept.
DICA
Você pode dar uma olhada na base de código do ExceptionHandler e ver como as exceções de validação são convertidas em uma resposta HTTP.
Além disso, o middleware de sessão substitui o método renderValidationErrorAsHTML
e usa mensagens flash para compartilhar os erros de validação com o formulário.
- SimpleErrorReporter.
- Especificações JSON API.
- Receberá os erros via sessão
- Todas as outras solicitações receberão erros de volta como texto simples.
O método request.validateUsing
A maneira recomendada de executar validações dentro de controladores é usar o método request.validateUsing
. Ao usar o método request.validateUsing
, você não precisa definir os dados de validação explicitamente; o corpo da solicitação, os valores da sequência de consulta e os arquivos são mesclados e passados como dados para o validador.
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)
}
}
Validando cookies, cabeçalhos e parâmetros de rota
Ao usar o método request.validateUsing
, você pode validar cookies, cabeçalhos e parâmetros de rota da seguinte forma.
const validator = vine.compile(
vine.object({
// Campos no corpo da solicitação
username: vine.string(),
password: vine.string(),
// Validar cookies
cookies: vine.object({
}),
// Validar cabeçalhos
headers: vine.object({
}),
// Validar parâmetros de rota
params: vine.object({
}),
})
)
await request.validateUsing(validator)
Passando metadados para validadores
Como os validadores são definidos fora do ciclo de vida da solicitação, eles não têm acesso direto aos dados da solicitação. Isso geralmente é bom porque torna os validadores reutilizáveis fora do ciclo de vida de uma solicitação HTTP.
No entanto, se um validador precisar acessar alguns dados de tempo de execução, você deve passá-los como metadados durante a chamada do método validate
.
Vamos dar um exemplo da regra de validação unique
. Queremos garantir que o e-mail do usuário seja único no banco de dados, mas pular a linha para o usuário conectado no momento.
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
})
})
)
No exemplo acima, acessamos o usuário conectado no momento por meio da propriedade meta.userId
. Vamos ver como podemos passar o userId
durante uma solicitação HTTP.
async update({ request, auth }: HttpContext) {
await request.validateUsing(
updateUserValidator,
{
meta: {
userId: auth.user!.id
}
}
)
}
Tornando os metadados seguros para o tipo
No exemplo anterior, devemos lembrar de passar o meta.userId
durante a validação. Seria ótimo se pudéssemos fazer o TypeScript nos lembrar do mesmo.
No exemplo a seguir, usamos a função vine.withMetaData
para definir o tipo estático dos metadados que esperamos usar em nosso esquema.
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
})
})
)
Observe que o VineJS não valida os metadados em tempo de execução. No entanto, se quiser fazer isso, você pode passar um retorno de chamada para o método withMetaData
e executar a validação manualmente.
vine.withMetaData<{ userId: number }>((meta) => {
// valide o metadata
})
Configurando o VineJS
Você pode criar um arquivo de pré-carregamento dentro do diretório start
para configurar o VineJS com mensagens de erro personalizadas ou usar um relator de erro personalizado.
node ace make:preload validator
No exemplo a seguir, nós definimos mensagens de erro personalizadas.
// start/validator.ts
import vine, { SimpleMessagesProvider } from '@vinejs/vine'
vine.messagesProvider = new SimpleMessagesProvider({
// Aplicável para todos os campos
'required': 'The {{ field }} field is required',
'string': 'The value of {{ field }} field must be a string',
'email': 'The value is not a valid email address',
// Mensagem de erro para o campo de nome de usuário
'username.required': 'Please choose a username for your account',
})
No exemplo a seguir, nós registramos um relator de erro personalizado.
// start/validator.ts
import vine, { SimpleMessagesProvider } from '@vinejs/vine'
import { JSONAPIErrorReporter } from '../app/validation_reporters.js'
vine.errorReporter = () => new JSONAPIErrorReporter()
Regras contribuídas pelo AdonisJS
A seguir está a lista de regras do VineJS contribuídas pelo AdonisJS.
O tipo de esquema vine.file
é adicionado pelo pacote principal do AdonisJS.
O que vem a seguir?
- Mensagens personalizadas no VineJS.
- Relatores de erro no VineJS.
- Schema API.
- Traduções i18n para definir mensagens de erro de validação.