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"
}