Poprzednie sekcje przedstawiały metodę bezpośredniego odczytywania parametrów żądania. Jeśli uciążliwe jest odczytywanie każdego parametru osobno, framework iris zapewnia również mechanizm wiązania parametrów, który umoże wiązać parametry żądania z typem struktury, obsługując również mechanizm walidacji parametrów formularza.
Wiązanie modelu i walidacja
Aby powiązać ciało żądania z typem, należy użyć wiązania modelu. Obecnie obsługujemy wiązanie typów takich jak JSON
, JSONProtobuf
, Protobuf
, MsgPack
, XML
, YAML
oraz standardowe wartości formularza (foo=bar&boo=baz).
// Poniżej znajdują się definicje funkcji służące do wiązania parametrów żądania w różnych formatach z struct
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
Podczas korzystania z ReadBody
, Iris wywnioskował będzie wiązacza na podstawie nagłówka Content-Type. Jeśli jesteś pewien co do zawartości, którą chcesz powiązać, możesz użyć konkretnych metod ReadXXX
, takich jak ReadJSON
lub ReadProtobuf
.
ReadBody(ptr interface{}) error
Iris posiada inteligentną wbudowaną walidację danych. Jednak pozwala ona dołączyć walidator, który będzie automatycznie wywoływany w przypadku metod takich jak ReadJSON
, ReadXML
itp. W tym przykładzie dowiemy się, jak wykorzystać go-playground/validator/v10 do walidacji treści żądania.
Zauważ, że musisz ustawić odpowiednie tagi wiązania na wszystkich polach do powiązania. Na przykład, podczas wiązania z JSON, ustaw json:"nazwa_pola"
.
Możesz również określić określone pola jako wymagane. Jeśli pole ma dekorację binding:"required"
i nie zostanie podana wartość podczas wiązania, zostanie zwrócony błąd.
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("/użytkownik")
{
userRouter.Get("/błędy-walidacji", rozwiązanieDokumentacjiBłędów)
userRouter.Post("/", dodajUżytkownika)
}
app.Listen(":8080")
}
// Użytkownik zawiera informacje o użytkowniku.
type Użytkownik struct {
Imię string `json:"imie" validate:"required"` // Imię, wymagane
Nazwisko string `json:"nazwisko" validate:"required"` // Nazwisko, wymagane
Wiek uint8 `json:"wiek" validate:"gte=0,lte=130"` // Wiek, zakres od 0 do 130
Email string `json:"email" validate:"required,email"` // Email, wymagany
UlubionyKolor string `json:"ulubionyKolor" validate:"hexcolor|rgb|rgba"` // Ulubiony kolor, musi być legalną wartością szesnastkową, RGB lub RGBA
Adresy []*Adres `json:"adresy" validate:"required,dive,required"` // Lista adresów, nie może być pusta i każdy element adresu jest wymagany
}
// Adres przechowuje informacje o adresie użytkownika.
type Adres struct {
Ulica string `json:"ulica" validate:"required"` // Ulica, wymagana
Miasto string `json:"miasto" validate:"required"` // Miasto, wymagane
Planeta string `json:"planeta" validate:"required"` // Planeta, wymagana
Telefon string `json:"telefon" validate:"required"` // Telefon, wymagany
}
type błądWalidacji struct {
ActualTag string `json:"tag"` // Aktualny tag
Namespace string `json:"namespace"` // Przestrzeń nazw
Kind string `json:"kind"` // Typ
Type string `json:"type"` // Rodzaj
Value string `json:"value"` // Wartość
Param string `json:"param"` // Parametr
}
func wrapBłędyWalidacji(errs validator.ValidationErrors) []błądWalidacji {
validationErrors := make([]błądWalidacji, 0, len(errs))
for _, validationErr := range errs {
validationErrors = append(validationErrors, błądWalidacji{
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 dodajUżytkownika(ctx iris.Context) {
var użytkownik Użytkownik
err := ctx.ReadJSON(&użytkownik)
if err != nil {
// Obsługa błędów, poniżej znajduje się poprawny sposób...
if errs, ok := err.(validator.ValidationErrors); ok {
// Owijanie błędów w formacie JSON, podstawowa biblioteka zwraca błędy typu interfejsowego.
błędyWalidacji := wrapBłędyWalidacji(errs)
// Zwracanie odpowiedzi aplikacji w formacie application/json+problem i przerwanie wykonywania kolejnych handlerów
ctx.StopWithProblem(iris.StatusBadRequest, iris.NewProblem().
Title("Błędy walidacji").
Detail("Jeden lub więcej pól nie przeszło walidacji").
Type("/użytkownik/błędy-walidacji").
Key("błędy", błędyWalidacji))
return
}
// Może to być wewnętrzny błąd JSON, tutaj nie ma dalszych informacji.
ctx.StopWithStatus(iris.StatusInternalServerError)
return
}
ctx.JSON(iris.Map{"message": "OK"})
}
func rozwiązanieDokumentacjiBłędów(ctx iris.Context) {
ctx.WriteString("Ta strona służy do wyjaśnienia, jak rozwiązywać błędy walidacji dla programistów sieciowych lub użytkowników interfejsu API")
}
{
"tytuł": "Błąd walidacji",
"szczegóły": "Jedno lub więcej pól nie przeszło walidacji",
"typ": "http://localhost:8080/użytkownik/błędy-walidacji",
"status": 400,
"pola": [
{
"tag": "wymagane",
"przestrzeńNazw": "Użytkownik.Imię",
"rodzaj": "string",
"typ": "string",
"wartość": "",
"parametr": ""
},
{
"tag": "wymagane",
"przestrzeńNazw": "Użytkownik.Nazwisko",
"rodzaj": "string",
"typ": "string",
"wartość": "",
"parametr": ""
}
]
}
Bindowanie parametrów zapytania URL
Metoda ReadQuery
wiąże tylko parametry zapytania, a nie dane ciała żądania. Użyj ReadForm
, aby wiązać dane ciała żądania.
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("Person: %#+v", person)
ctx.WriteString("Success")
}
Wiązanie dowolnych danych
Wiąż żądanie ciała do "ptr" w oparciu o typ zawartości danych wysłanych przez klienta, takie jak JSON, XML, YAML, MessagePack, Protobuf, Form i zapytanie URL.
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("Person: %#+v", person)
ctx.WriteString("Success")
}
Możesz przetestować za pomocą następującej komendy:
$ curl -X GET "localhost:8085/testing?name=kataras&address=xyz&birthday=1992-03-15&createTime=1562400033000000123&unixTime=1562400033"
Wiązanie parametrów ścieżki 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")
}
Żądanie
$ curl -v http://localhost:8080/kataras/27/iris/web/framework
Wiązanie parametrów nagłówka żądania
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")
}
Żądanie
curl -H "x-request-id:373713f0-6b4b-42ea-ab9f-e2e04bc38e73" -H "authentication: Bearer my-token" \
http://localhost:8080
Odpowiedź
{
"RequestID": "373713f0-6b4b-42ea-ab9f-e2e04bc38e73",
"Authentication": "Bearer my-token"
}