Ir para o conteúdo
Trilha
Capítulos

Referência

Validação

A tag validate, as regras por tipo, as suas próprias regras e as mensagens que o Bind devolve.

O Bind valida enquanto preenche: depois de converter os valores, aplica a tag validate de cada campo e devolve FieldErrors (campo → mensagem) com tudo o que falhou. As mesmas regras valem para formulário e para JSON — com corpo JSON o campo é nomeado pela tag json, que é o nome que o cliente reconhece.

type entrada struct {
	Nome      string    `form:"nome" validate:"required,min=3,max=80"`
	Email     string    `form:"email" validate:"required,email"`
	Confirma  string    `form:"confirma" validate:"eqfield=email"`
	Data      time.Time `form:"data" validate:"required,min=2026-01-01"`
	Plano     string    `form:"plano" validate:"oneof=gratis pro"`
	Desconto  *int      `form:"desconto" validate:"required,min=0"`
}

Regras#

RegraTextoNúmerotime.Time[]string (checkbox, select)
requirednão vazioqualquer valor; 0 só por ponteironão é a data zeroao menos um
min=nao menos n caracteresvalor >= ndata não é antes de n (2006-01-02)ao menos n escolhidos
max=nno máximo n caracteresvalor <= ndata não é depois de nno máximo n escolhidos
len=nexatamente n caracteresexatamente n escolhidos
emailum @, domínio com ponto
urlhttp/https absoluta
oneof=a b cvalor é uma das opções, separadas por espaçoigual, como texto
eqfield=outroigual ao valor do outro campo, pelo nome de formulárioigualigual

As regras são separadas por vírgula e aplicadas em ordem; a primeira que falha é a mensagem daquele campo. Toda regra além de required ignora valor vazio, então campo opcional só responde pelo que alguém digitou. Valor que nem converte (abc num int) recebe trilha.BindInvalid e nenhuma mensagem de regra — uma mensagem por campo.

required é o valor zero: 0, false, "" e a data zero não passam. Onde zero é resposta de verdade, declare o campo como ponteiro: um *int que chegou com 0 está presente, e só o campo ausente falha.

Regras suas#

SímboloPapel
trilha.Validatorinterface{ Validate() error }: o valor se confere
trilha.AddRule(nome, func(Field) bool)registra um nome para a tag; nome repetido entra em pânico
trilha.Fieldo que a regra vê: Name, Param, Text, Value, Other(nome)
trilha.ValidationMessagesmap[string]string das mensagens; {param} é substituído
trilha.UseValidationPTBR()troca as mensagens, BindInvalid incluído, para português

Campo cujo tipo tem Validate() error é conferido depois de as regras da tag passarem, e a mensagem do erro vai para FieldErrors como está (receptor por valor ou por ponteiro, os dois funcionam). A struct também pode ter Validate() error: roda no fim, só quando nenhum campo falhou — é o que torna segura uma conferência que lê dois campos. Ela pode devolver FieldErrors para dizer de quem é a culpa; qualquer outro erro volta do Bind intacto.

trilha.AddRule("cep", func(f trilha.Field) bool { return cepValido(f.Text) })
trilha.ValidationMessages["cep"] = "CEP inválido"

Field.Value é o valor convertido (string, bool, int64, float64, time.Time, []string, ou nil quando o campo não veio) e Field.Text é a mesma coisa como texto, que é tudo de que a maioria das regras precisa. Regra que compara campos lê o outro com f.Other("email").

Onde a validação para#

A tag diz o que um valor aceita. Se a conta existe, se a sala está livre nessa noite, se essa pessoa pode fazer isso — essas leem os seus dados e são do seu pacote. Rode depois do Bind e junte no mesmo FieldErrors, para todas as mensagens chegarem numa resposta só:

errs := trilha.FieldErrors{}
if err := c.Bind(&in); err != nil {
	fe, ok := err.(trilha.FieldErrors)
	if !ok {
		return err
	}
	errs = fe
}
for campo, msg := range plano.Validar(&in) {
	errs.Add(campo, msg)
}
if errs.Any() {
	return c.Render(http.StatusUnprocessableEntity, pagina(c, in, errs))
}

Nome de regra que ninguém registrou entra em pânico na primeira requisição que passa pelo campo, de propósito: um erro de digitação na tag seria, senão, um formulário que aceita qualquer coisa em produção.