Introdução
O AdonisJS tem suporte de primeira classe para analisar e validar o corpo da solicitação, e não há necessidade de instalar nenhum pacote de terceiros para o mesmo. Basta definir o esquema de validação e validar o corpo da solicitação em relação a ele.
import Route from '@ioc:Adonis/Core/Route'
import { schema } from '@ioc:Adonis/Core/Validator'
Route.post('posts', async ({ request }) => {
/**
* Definição de esquema
*/
// const newPostSchema = schema.create({schema.string(),
body: schema.string(),
categories: schema.array().members(schema.number()),
})
/**
* Validar o corpo da solicitação em relação ao esquema
*/
const payload = await request.validate({ schema: newPostSchema })
})
O validador também extrai os tipos estáticos da definição do esquema. Você obtém as validações de tempo de execução e a segurança do tipo estático de uma única definição de esquema.
Composição do esquema
A definição do esquema é dividida em três partes principais.
- O método
schema.create
define o formato dos dados que você espera. - Os métodos
schema.string
,schema.number
e outros semelhantes definem o tipo de dados para um campo individual. - Finalmente, você usa o objeto
rules
para aplicar restrições de validação adicionais em um determinado campo. Por exemplo: Validar uma string para ser um e-mail válido é único dentro do banco de dados.
NOTA
O objeto rules
é importado de @ioc:Adonis/Core/Validator
import { schema, rules } from '@ioc:Adonis/Core/Validator'
Se você olhar com cuidado, separamos as validações de formato dos tipos de dados principais. Então, por exemplo, não há nenhum tipo de dado chamado schema.email
. Em vez disso, usamos o método rules.email
para garantir que uma string seja formatada como um e-mail.
Essa separação ajuda a estender o validador com regras personalizadas sem criar tipos de esquema desnecessários que não têm significado. Por exemplo, não há nada chamado tipo de e-mail; é apenas uma string, formatada como um e-mail.
Trabalhando com valores opcionais e nulos
Todos os campos são obrigatórios por padrão. No entanto, você pode usar os modificadores optional
, nullable
e nullableAndOptional
para marcar campos como opcionais.
Todos esses modificadores atendem a propósitos diferentes. Vamos dar uma olhada mais de perto neles.
Modificador | Comportamento da validação | Retorna payload |
---|---|---|
optional | Permite que valores null e undefined existam | Remove a chave do payload de retorno se não for inexistente |
nullable | Permite que valores null existam. No entanto, o campo deve ser definido nos dados de validação | Retorna o valor do campo, incluindo nulo. |
nullableAndOptional | Permite que valores null e undefined existam. (O mesmo que o modificador 1) | Remove a chave somente quando o valor é indefinido, caso contrário, retorna o valor do campo |
Caso de uso para o modificador nullable
Você frequentemente se verá usando o modificador nullable
para permitir campos opcionais dentro dos seus formulários de aplicação.
No exemplo a seguir, quando o usuário envia um valor vazio para o campo fullName
, o servidor receberá null
e, portanto, você pode atualizar seu nome completo existente dentro do banco de dados para nulo.
schema: schema.create({
fullName: schema.string.nullable(),
})
Caso de uso para o modificador nullableAndOptional
Se você criar um servidor de API que aceita solicitações PATCH e permite que o cliente atualize uma parte de um recurso, você deve usar o modificador nullableAndOptional
.
No exemplo a seguir, se o fullName
for indefinido, você pode assumir que o cliente não deseja atualizar esta propriedade e, se for null
, ele deseja definir o valor da propriedade de null
.
const payload = await request.validate({
schema: schema.create({
fullName: schema.string.nullableAndOptional(),
})
})
const user = await User.findOrFail(1)
user.merge(payload)
await user.save()
Caso de uso para modificador optional
O modificador optional
é útil se você deseja atualizar uma parte de um recurso sem campos opcionais.
A propriedade email
pode ou não existir no exemplo a seguir. Mas o usuário não pode defini-la como null
. Se a propriedade não estiver na solicitação, você não atualizará o e-mail.
const payload = await request.validate({
schema: schema.create({
email: schema.string.optional(),
})
})
const user = await User.findOrFail(1)
user.merge(payload)
await user.save()
Validando solicitações HTTP
Você pode validar o corpo da solicitação, a string de consulta e os parâmetros de rota para uma determinada solicitação HTTP usando o método request.validate
. Em caso de falha, o método validate
gerará uma exceção.
import Route from '@ioc:Adonis/Core/Route'
import { schema, rules } from '@ioc:Adonis/Core/Validator'
Route.post('users', async ({ request, response }) => {
/**
* Etapa 1 - Definir esquema
*/
const newUserSchema = schema.create({
username: schema.string(),
email: schema.string([
rules.email()
]),
password: schema.string([
rules.confirmed(),
rules.minLength(4)
])
})
try {
/**
* Etapa 2 - Validar o corpo da solicitação em relação
* ao esquema
*/
const payload = await request.validate({
schema: newUserSchema
})
} catch (error) {
/**
* Etapa 3 - Lidar com erros
*/
response.badRequest(error.messages)
}
})
Recomendamos NÃO autotratar a exceção e deixar o AdonisJS converter a exceção em uma resposta usando negociação de conteúdo.
A seguir, uma explicação de como a negociação de conteúdo funciona.
Aplicativo renderizado pelo servidor
Se você criar um aplicativo da web padrão com modelos do lado do servidor, redirecionaremos o cliente de volta ao formulário e passaremos os erros como mensagens flash da sessão.
A seguir, a estrutura das mensagens de erro dentro do armazenamento flash da sessão.
{
errors: {
username: ['username is required']
}
}
Você pode acessá-las usando o auxiliar global flashMessages
.
@if(flashMessages.has('errors.username'))
<p> {{ flashMessages.get('errors.username') }} </p>
@end
Solicitações com cabeçalho Accept=application/json
Solicitações negociando o tipo de dados JSON recebem as mensagens de erro como uma matriz de objetos. Cada mensagem de erro contém o nome do campo, a regra de validação com falha e a mensagem de erro.
{
errors: [
{
field: 'title',
rule: 'required',
message: 'required validation failed',
},
]
}
API JSON
Solicitações negociando usando o cabeçalho Accept=application/vnd.api+json
recebem as mensagens de erro conforme a especificação da API JSON.
{
errors: [
{
code: 'required',
source: {
pointer: 'title',
},
// 'required validation failed'
}
]
}
Uso do validador autônomo
Você também pode usar o validador fora de uma solicitação HTTP importando o método validate
do módulo Validator. A API funcional permanece a mesma. No entanto, você terá que fornecer manualmente os data
para validar.
import { validator, schema } from '@ioc:Adonis/Core/Validator'
await validator.validate({
schema: schema.create({
// ... define o esquema
}),
data: {
email: 'virk@adonisjs.com',
password: 'secret'
}
})
Além disso, como você realiza a validação fora de uma solicitação HTTP, você terá que lidar com a exceção e exibir os erros manualmente.
Classes validadoras
As classes validadoras permitem que você extraia o esquema inline de seus controladores e os mova para uma classe dedicada.
Você pode criar um novo validador executando o seguinte comando Ace.
node ace make:validator CreateUser
# CREATE: app/Validators/CreateUserValidator.ts
Todas as propriedades relacionadas à validação, incluindo schema
, messages
são definidas como propriedades na classe.
// app/Validators/CreateUserValidator.ts
import { schema, CustomMessages } from '@ioc:Adonis/Core/Validator'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class CreateUserValidator {
constructor (protected ctx: HttpContextContract) {
}
public schema = schema.create({
})
public messages: CustomMessages = {}
}
Usando o validador
Em vez de passar um objeto com a propriedade schema
, agora você pode passar o construtor de classe para o método request.validate
.
import Route from '@ioc:Adonis/Core/Route'
import CreateUser from 'App/Validators/CreateUserValidator'
Route.post('users', async ({ request, response }) => {
const payload = await request.validate(CreateUser)
})
Durante a validação, uma nova instância da classe validadora é criada nos bastidores. Além disso, o método request.validate
passará o contexto HTTP atual como um primeiro argumento do construtor.
Você também pode construir manualmente a instância da classe e passar quaisquer argumentos que desejar. Por exemplo:
Route.post('users', async ({ request, response }) => {
const payload = await request.validate(
new CreateUser({
countries: fetchAllowedCountries(),
states: fetchAllowedStates()
})
)
})
A seguir está um exemplo de uso das classes validadoras fora da solicitação HTTP.
import { validator } from '@ioc:Adonis/Core/Validator'
import CreateUser from 'App/Validators/CreateUserValidator'
await validator.validate(
new CreateUser({
countries: fetchAllowedCountries(),
states: fetchAllowedStates()
})
)