Le sezioni precedenti hanno introdotto il metodo di lettura diretta dei parametri della richiesta. Se risulta complicato leggere singolarmente ciascun parametro, il framework iris fornisce anche un meccanismo di binding dei parametri, che consente di associare i parametri della richiesta a una struttura, e supporta anche un meccanismo di validazione dei parametri del modulo.
Associazione dei modelli e convalida
Per associare il corpo della richiesta a un tipo, utilizzare l'associazione dei modelli. Attualmente supportiamo tipi di associazione come JSON
, JSONProtobuf
, Protobuf
, MsgPack
, XML
, YAML
e valori di modulo standard (foo=bar&boo=baz).
// Di seguito sono riportate le definizioni di funzione per l'associazione dei parametri della richiesta di vari formati a una struttura
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
Quando si utilizza ReadBody
, Iris dedurrà l'associazione in base all'intestazione Content-Type. Se si è certi del contenuto che si desidera associare, è possibile utilizzare metodi specifici come ReadJSON
o ReadProtobuf
.
ReadBody(ptr interface{}) error
Iris dispone di un'intelligente convalida dati integrata. Tuttavia, consente di allegare un validatore che verrà chiamato automaticamente nei metodi come ReadJSON
, ReadXML
, ecc. In questo esempio, vedremo come utilizzare go-playground/validator/v10 per convalidare il corpo della richiesta.
Si tenga presente che è necessario impostare le etichette di associazione corrispondenti su tutti i campi da associare. Ad esempio, quando si associa da JSON, impostare json:"nomecampo"
.
È inoltre possibile specificare determinati campi come campi obbligatori. Se un campo ha la decorazione binding:"required"
e durante l'associazione non viene fornito alcun valore, verrà restituito un errore.
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("/utente")
{
userRouter.Get("/errori-di-validazione", resolveErrorsDocumentation)
userRouter.Post("/", postUser)
}
app.Listen(":8080")
}
// User contiene le informazioni dell'utente.
type User struct {
Nome string `json:"nome" validate:"required"` // Nome, obbligatorio
Cognome string `json:"cognome" validate:"required"` // Cognome, obbligatorio
Età uint8 `json:"età" validate:"gte=0,lte=130"` // Età, compresa tra 0 e 130
Email string `json:"email" validate:"required,email"` // Email, obbligatoria
ColorePreferito string `json:"colorePref" validate:"hexcolor|rgb|rgba"` // Colore preferito, deve essere un valore di colore esadecimale, RGB o RGBA legale
Indirizzi []*Indirizzo `json:"indirizzi" validate:"required,dive,required"` // Elenco degli indirizzi, non può essere vuoto e ciascun elemento dell'indirizzo è obbligatorio
}
// Indirizzo memorizza le informazioni dell'indirizzo dell'utente.
type Indirizzo struct {
Via string `json:"via" validate:"required"` // Via, obbligatoria
Città string `json:"città" validate:"required"` // Città, obbligatoria
Pianeta string `json:"pianeta" validate:"required"` // Pianeta, obbligatorio
Telefono string `json:"telefono" validate:"required"` // Telefono, obbligatorio
}
type erroreDiValidazione struct {
TagEffettivo string `json:"tag"` // Tag effettivo
SpazioNomi string `json:"spazioNomi"` // Spazio dei nomi
Tipo string `json:"tipo"` // Tipo
Valore string `json:"valore"` // Valore
Parametro string `json:"parametro"` // Parametro
}
func wrapErroriDiValidazione(errs validator.ValidationErrors) []erroreDiValidazione {
erroriDiValidazione := make([]erroreDiValidazione, 0, len(errs))
for _, erroreDiValidazione := range errs {
erroriDiValidazione = append(erroriDiValidazione, erroreDiValidazione{
TagEffettivo: erroreDiValidazione.TagEffettivo(),
SpazioNomi: erroreDiValidazione.SpazioNomi(),
Tipo: erroreDiValidazione.Tipo().String(),
Valore: fmt.Sprintf("%v", erroreDiValidazione.Valore()),
Parametro: erroreDiValidazione.Parametro(),
})
}
return erroriDiValidazione
}
func postUser(ctx iris.Context) {
var utente User
err := ctx.ReadJSON(&utente)
if err != nil {
// Gestire gli errori, quanto segue è il modo corretto...
if errs, ok := err.(validator.ValidationErrors); ok {
// Incapsulare gli errori nel formato JSON, la libreria sottostante restituisce errori di tipo interfaccia.
erroriDiValidazione := wrapErroriDiValidazione(errs)
// Restituisce una risposta application/json+problem e interrompe l'esecuzione degli handler successivi
ctx.StopWithProblem(iris.StatusBadRequest, iris.NewProblem().
Title("Errori di Validazione").
Detail("Uno o più campi non hanno superato la validazione").
Type("/utente/errori-di-validazione").
Key("errori", erroriDiValidazione))
return
}
// Potrebbe essere un errore JSON interno, qui non vengono fornite ulteriori informazioni.
ctx.StopWithStatus(iris.StatusInternalServerError)
return
}
ctx.JSON(iris.Map{"messaggio": "OK"})
}
func resolveErrorsDocumentation(ctx iris.Context) {
ctx.WriteString("Questa pagina è utilizzata per spiegare come risolvere gli errori di validazione agli sviluppatori web o agli utenti API")
}
{
"fname": "",
"lname": "",
"age": 45,
"email": "[email protected]",
"favColor": "#000",
"addresses": [{
"street": "Eavesdown Docks",
"planet": "Persphone",
"phone": "none",
"city": "Unknown"
}]
}
Esempio di richiesta
{
"nome": "",
"cognome": "",
"età": 45,
"email": "[email protected]",
"colorePreferito": "#000",
"indirizzi": [{
"via": "Banchine di Eavesdown",
"planeta": "Persphone",
"telefono": "nessuno",
"città": "Sconosciuta"
}]
}
Esempio di risposta
{
"titolo": "Errore di convalida",
"dettaglio": "Uno o più campi non hanno superato la convalida",
"tipo": "http://localhost:8080/user/validation-errors",
"stato": 400,
"campi": [
{
"tag": "richiesto",
"namespace": "User.FirstName",
"tipo": "string",
"valore": "",
"parametro": ""
},
{
"tag": "richiesto",
"namespace": "User.LastName",
"tipo": "string",
"valore": "",
"parametro": ""
}
]
}
Binding URL query parameters
Il metodo ReadQuery
lega solo i parametri della query, non i dati del corpo della richiesta. Usa ReadForm
per legare i dati del corpo della richiesta.
package main
import "github.com/kataras/iris/v12"
type Person struct {
Name string `url:"name,required"`
Address string `url:"address"`
}
func main() {
app := iris.Default()
app.Any("/", index)
app.Listen(":8080")
}
func index(ctx iris.Context) {
var person Person
if err := ctx.ReadQuery(&person); err != nil {
ctx.StopWithError(iris.StatusBadRequest, err)
return
}
ctx.Application().Logger().Infof("Persona: %#+v", person)
ctx.WriteString("Successo")
}
Binding dati arbitrari
Legare il corpo della richiesta a "ptr" in base al tipo di contenuto dei dati inviati dal client, come JSON, XML, YAML, MessagePack, Protobuf, Form e URL query.
package main
import (
"time"
"github.com/kataras/iris/v12"
)
type Person struct {
Name string `form:"name" json:"name" url:"name" msgpack:"name"`
Address string `form:"address" json:"address" url:"address" msgpack:"address"`
Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1" json:"birthday" url:"birthday" msgpack:"birthday"`
CreateTime time.Time `form:"createTime" time_format:"unixNano" json:"create_time" url:"create_time" msgpack:"createTime"`
UnixTime time.Time `form:"unixTime" time_format:"unix" json:"unix_time" url:"unix_time" msgpack:"unixTime"`
}
func main() {
app := iris.Default()
app.Any("/", index)
app.Listen(":8080")
}
func index(ctx iris.Context) {
var person Person
if err := ctx.ReadBody(&person); err != nil {
ctx.StopWithError(iris.StatusBadRequest, err)
return
}
ctx.Application().Logger().Infof("Persona: %#+v", person)
ctx.WriteString("Successo")
}
Puoi testare con il seguente comando:
$ curl -X GET "localhost:8085/testing?name=kataras&address=xyz&birthday=1992-03-15&createTime=1562400033000000123&unixTime=1562400033"
Binding parametri del percorso URL
package main
import "github.com/kataras/iris/v12"
type myParams struct {
Name string `param:"name"`
Age int `param:"age"`
Tail []string `param:"tail"`
}
func main() {
app := iris.Default()
app.Get("/{name}/{age:int}/{tail:path}", func(ctx iris.Context) {
var p myParams
if err := ctx.ReadParams(&p); err != nil {
ctx.StopWithError(iris.StatusInternalServerError, err)
return
}
ctx.Writef("myParams: %#v", p)
})
app.Listen(":8088")
}
Richiesta
$ curl -v http://localhost:8080/kataras/27/iris/web/framework
Binding parametri della richiesta header
package main
import "github.com/kataras/iris/v12"
type myHeaders struct {
RequestID string `header:"X-Request-Id,required"`
Authentication string `header:"Authentication,required"`
}
func main() {
app := iris.Default()
r.GET("/", func(ctx iris.Context) {
var hs myHeaders
if err := ctx.ReadHeaders(&hs); err != nil {
ctx.StopWithError(iris.StatusInternalServerError, err)
return
}
ctx.JSON(hs)
})
app.Listen(":8080")
}
Richiesta
curl -H "x-request-id:373713f0-6b4b-42ea-ab9f-e2e04bc38e73" -H "authentication: Bearer my-token" \
http://localhost:8080
Risposta
{
"RequestID": "373713f0-6b4b-42ea-ab9f-e2e04bc38e73",
"Authentication": "Bearer my-token"
}