As seções anteriores apresentaram o método de leitura direta dos parâmetros da solicitação. Se for complicado ler cada parâmetro individualmente, o framework iris também fornece um mecanismo de ligação de parâmetros, que pode ligar os parâmetros da solicitação a uma estrutura, e também suporta um mecanismo de validação de parâmetros de formulário.
Vinculação de modelo e Validação
Para vincular o corpo da requisição a um tipo, utilize a vinculação de modelo. Atualmente, oferecemos suporte à vinculação de tipos como JSON
, JSONProtobuf
, Protobuf
, MsgPack
, XML
, YAML
e valores de formulário padrão (foo=bar&boo=baz).
// Abaixo estão as definições de funções para vincular parâmetros de requisição de vários formatos a uma estrutura
ReadJSON(outPtr interface{}) error
ReadJSONProtobuf(ptr proto.Message, opts ...ProtoUnmarshalOptions) error
ReadProtobuf(ptr proto.Message) error
ReadMsgPack(ptr interface{}) error
ReadXML(outPtr interface{}) error
ReadYAML(outPtr interface{}) error
ReadForm(formObject interface{}) error
ReadQuery(ptr interface{}) error
Ao usar o ReadBody
, o Iris inferirá o vinculador com base no cabeçalho Content-Type. Se você tem certeza sobre o conteúdo que deseja vincular, pode usar métodos específicos como ReadJSON
ou ReadProtobuf
.
ReadBody(ptr interface{}) error
O Iris vem com uma validação de dados integrada e inteligente. No entanto, permite que você anexe um validador que será chamado automaticamente em métodos como ReadJSON
, ReadXML
, etc. Neste exemplo, aprenderemos como usar o go-playground/validator/v10 para validar o corpo da requisição.
Por favor, note que é necessário definir as tags de vinculação correspondentes em todos os campos a serem vinculados. Por exemplo, ao vincular do JSON, defina json:"nomedocampo"
.
Também é possível especificar certos campos como campos obrigatórios. Se um campo tiver a decoração binding:"required"
e nenhum valor for fornecido durante a vinculação, um erro será retornado.
package main
import (
"fmt"
"github.com/kataras/iris/v12"
"github.com/go-playground/validator/v10"
)
func main() {
app := iris.New()
app.Validator = validator.New()
userRouter := app.Party("/user")
{
userRouter.Get("/validation-errors", resolveErrorsDocumentation)
userRouter.Post("/", postUser)
}
app.Listen(":8080")
}
// User contains user information.
type User struct {
FirstName string `json:"fname" validate:"required"` // Primeiro nome, obrigatório
LastName string `json:"lname" validate:"required"` // Último nome, obrigatório
Age uint8 `json:"age" validate:"gte=0,lte=130"` // Idade, intervalo entre 0 e 130
Email string `json:"email" validate:"required,email"` // Email, obrigatório
FavoriteColor string `json:"favColor" validate:"hexcolor|rgb|rgba"` // Cor favorita, deve ser um valor hexadecimal, RGB, ou RGBA válido
Addresses []*Address `json:"addresses" validate:"required,dive,required"` // Lista de endereços, não deve estar vazia e cada item de endereço é obrigatório
}
// Address stores user address information.
type Address struct {
Street string `json:"street" validate:"required"` // Rua, obrigatório
City string `json:"city" validate:"required"` // Cidade, obrigatório
Planet string `json:"planet" validate:"required"` // Planeta, obrigatório
Phone string `json:"phone" validate:"required"` // Telefone, obrigatório
}
type validationError struct {
ActualTag string `json:"tag"` // Tag atual
Namespace string `json:"namespace"` // Namespace
Kind string `json:"kind"` // Tipo
Type string `json:"type"` // Tipo
Value string `json:"value"` // Valor
Param string `json:"param"` // Parâmetro
}
func wrapValidationErrors(errs validator.ValidationErrors) []validationError {
validationErrors := make([]validationError, 0, len(errs))
for _, validationErr := range errs {
validationErrors = append(validationErrors, validationError{
ActualTag: validationErr.ActualTag(),
Namespace: validationErr.Namespace(),
Kind: validationErr.Kind().String(),
Type: validationErr.Type().String(),
Value: fmt.Sprintf("%v", validationErr.Value()),
Param: validationErr.Param(),
})
}
return validationErrors
}
func postUser(ctx iris.Context) {
var user User
err := ctx.ReadJSON(&user)
if err != nil {
// Lidar com erros, o seguinte é o modo correto...
if errs, ok := err.(validator.ValidationErrors); ok {
// Envolver erros em formato JSON, a biblioteca subjacente retorna erros do tipo interface.
validationErrors := wrapValidationErrors(errs)
// Retornar uma resposta application/json+problem e interromper a execução dos manipuladores subsequentes
ctx.StopWithProblem(iris.StatusBadRequest, iris.NewProblem().
Title("Erros de Validação").
Detail("Um ou mais campos não passaram na validação").
Type("/user/validation-errors").
Key("errors", validationErrors))
return
}
// Pode ser um erro interno do JSON, nenhuma informação adicional fornecida aqui.
ctx.StopWithStatus(iris.StatusInternalServerError)
return
}
ctx.JSON(iris.Map{"message": "OK"})
}
func resolveErrorsDocumentation(ctx iris.Context) {
ctx.WriteString("Esta página é usada para explicar como resolver os erros de validação para desenvolvedores web ou usuários da API")
}
{
"title": "Erro de Validação",
"detail": "Um ou mais campos falharam na validação",
"type": "http://localhost:8080/user/validation-errors",
"status": 400,
"fields": [
{
"tag": "obrigatório",
"namespace": "Usuário.PrimeiroNome",
"kind": "string",
"type": "string",
"value": "",
"param": ""
},
{
"tag": "obrigatório",
"namespace": "Usuário.Sobrenome",
"kind": "string",
"type": "string",
"value": "",
"param": ""
}
]
}
Ligação de parâmetros de consulta de URL
O método ReadQuery
liga apenas os parâmetros de consulta, não os dados do corpo da solicitação. Use ReadForm
para ligar os dados do corpo da solicitação.
pacote principal
import "github.com/kataras/iris/v12"
type Pessoa struct {
Nome string `url:"nome,required"`
Endereço string `url:"endereço"`
}
func main() {
app := iris.Default()
app.Any("/", índice)
app.Listen(":8080")
}
func index(ctx iris.Context) {
var pessoa Pessoa
if err := ctx.ReadQuery(&pessoa); err != nil {
ctx.StopWithError(iris.StatusBadRequest, err)
return
}
ctx.Application().Logger().Infof("Pessoa: %#+v", pessoa)
ctx.WriteString("Sucesso")
}
Ligação de dados arbitrários
Vincule o corpo da solicitação a "ptr" com base no tipo de conteúdo dos dados enviados pelo cliente, como JSON, XML, YAML, MessagePack, Protobuf, Form e consulta de URL.
pacote principal
import (
"time"
"github.com/kataras/iris/v12"
)
type Pessoa struct {
Nome string `form:"nome" json:"nome" url:"nome" msgpack:"nome"`
Endereço string `form:"endereço" json:"endereço" url:"endereço" msgpack:"endereço"`
Aniversário time.Time `form:"aniversário" time_format:"2006-01-02" time_utc:"1" json:"aniversário" url:"aniversário" msgpack:"aniversário"`
HoraCriacao time.Time `form:"hora_criação" time_format:"unixNano" json:"hora_criação" url:"hora_criação" msgpack:"hora_criação"`
HoraUnix time.Time `form:"hora_unix" time_format:"unix" json:"hora_unix" url:"hora_unix" msgpack:"hora_unix"`
}
func main() {
app := iris.Default()
app.Any("/", índice)
app.Listen(":8080")
}
func index(ctx iris.Context) {
var pessoa Pessoa
if err := ctx.ReadBody(&pessoa); err != nil {
ctx.StopWithError(iris.StatusBadRequest, err)
return
}
ctx.Application().Logger().Infof("Pessoa: %#+v", pessoa)
ctx.WriteString("Sucesso")
}
Você pode testar com o seguinte comando:
$ curl -X GET "localhost:8085/testing?nome=kataras&endereço=xyz&aniversário=1992-03-15&hora_criação=1562400033000000123&hora_unix=1562400033"
Ligação de parâmetros de caminho de URL
pacote principal
import "github.com/kataras/iris/v12"
type meusParams struct {
Nome string `param:"nome"`
Idade int `param:"idade"`
Restante []string `param:"restante"`
}
func main() {
app := iris.Default()
app.Get("/{nome}/{idade:int}/{restante:path}", func(ctx iris.Context) {
var p meusParams
if err := ctx.ReadParams(&p); err != nil {
ctx.StopWithError(iris.StatusInternalServerError, err)
return
}
ctx.Writef("meusParams: %#v", p)
})
app.Listen(":8088")
}
Solicitação
$ curl -v http://localhost:8080/kataras/27/iris/web/framework
Ligação de parâmetros de solicitação de cabeçalho
pacote principal
import "github.com/kataras/iris/v12"
type meusHeaders struct {
IDSolicitação string `header:"X-ID-Solicitação,required"`
Autenticação string `header:"Autenticação,required"`
}
func main() {
app := iris.Default()
r.GET("/", func(ctx iris.Context) {
var hs meusHeaders
if err := ctx.ReadHeaders(&hs); err != nil {
ctx.StopWithError(iris.StatusInternalServerError, err)
return
}
ctx.JSON(hs)
})
app.Listen(":8080")
}
Solicitação
curl -H "x-id-solicitação:373713f0-6b4b-42ea-ab9f-e2e04bc38e73" -H "autenticação: Portador meu-token" \
http://localhost:8080
Resposta
{
"IDSolicitação": "373713f0-6b4b-42ea-ab9f-e2e04bc38e73",
"Autenticação": "Portador meu-token"
}