Die vorherigen Abschnitte haben die Methode des direkten Lesens von Anforderungsparametern vorgestellt. Wenn es umständlich ist, jeden Parameter einzeln zu lesen, bietet das Iris-Framework auch einen Parameter-Bindungsmechanismus an, der Anforderungsparameter an eine Struktur binden kann und zudem einen Mechanismus zur Validierung von Formparametern unterstützt.

Modellbindung und Validierung

Um den Anforderungskörper an einen Typ zu binden, verwenden Sie die Modellbindung. Derzeit unterstützen wir die Bindung von Typen wie JSON, JSONProtobuf, Protobuf, MsgPack, XML, YAML und Standardformularwerten (foo=bar&boo=baz).

// Unten sind die Funktionsdefinitionen für das Binden von Anforderungsparametern verschiedener Formate an eine Struktur
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

Bei Verwendung von ReadBody wird Iris den Binder anhand des Content-Type-Headers ableiten. Wenn Sie sicher sind, welchen Inhalt Sie binden möchten, können Sie spezifische ReadXXX-Methoden wie ReadJSON oder ReadProtobuf verwenden.

ReadBody(ptr interface{}) error

Iris verfügt über eine intelligente eingebaute Datenvalidierung. Sie können jedoch einen Validator anhängen, der automatisch bei Methoden wie ReadJSON, ReadXML usw. aufgerufen wird. In diesem Beispiel erfahren wir, wie man go-playground/validator/v10 verwendet, um den Anforderungskörper zu validieren.

Bitte beachten Sie, dass Sie entsprechende Bindungs-Tags für alle zu bindenden Felder festlegen müssen. Wenn Sie beispielsweise aus JSON binden, legen Sie "json:"fieldname"" fest.

Sie können auch bestimmte Felder als erforderlich kennzeichnen. Hat ein Feld die Dekoration binding:"required" und es wird kein Wert während der Bindung bereitgestellt, wird ein Fehler zurückgegeben.

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("/benutzer")
    {
        userRouter.Get("/validierungsfehler", resolveErrorsDocumentation)
        userRouter.Post("/", postUser)
    }
    app.Listen(":8080")
}

// Benutzer enthält Benutzerinformationen.
type Benutzer struct {
    Vorname        string     `json:"vname" validate:"required"` // Vorname, erforderlich
    Nachname       string     `json:"nname" validate:"required"` // Nachname, erforderlich
    Alter          uint8      `json:"alter" validate:"gte=0,lte=130"` // Alter, Bereich zwischen 0 und 130
    E-Mail         string     `json:"email" validate:"required,email"` // E-Mail, erforderlich
    Lieblingsfarbe string     `json:"lieblingsfarbe" validate:"hexcolor|rgb|rgba"` // Lieblingsfarbe, muss ein gültiger Hexadezimal-, RGB- oder RGBA-Farbwert sein
    Adressen       []*Adresse `json:"adressen" validate:"required,dive,required"` // Adressliste, darf nicht leer sein und jedes Adressenelement ist erforderlich
}

// Adresse speichert Adressinformationen des Benutzers.
type Adresse struct {
    Straße string `json:"straße" validate:"required"` // Straße, erforderlich
    Stadt  string `json:"stadt" validate:"required"` // Stadt, erforderlich
    Planet string `json:"planet" validate:"required"` // Planet, erforderlich
    Telefon  string `json:"telefon" validate:"required"` // Telefon, erforderlich
}

type validierungsfehler struct {
    AktuellesTag string `json:"tag"` // Aktuelles Tag
    Namespace string `json:"namespace"` // Namespace
    Art      string `json:"art"` // Art
    Typ      string `json:"typ"` // Typ
    Wert     string `json:"wert"` // Wert
    Parameter     string `json:"parameter"` // Parameter
}

func wrapValidationErrors(errs validator.ValidationErrors) []validierungsfehler {
    validierungsfehler := make([]validierungsfehler, 0, len(errs))
    for _, validationErr := range errs {
        validierungsfehler = append(validierungsfehler, validierungsfehler{
            AktuellesTag: validationErr.ActualTag(),
            Namespace: validationErr.Namespace(),
            Art:      validationErr.Kind().String(),
            Typ:      validationErr.Type().String(),
            Wert:     fmt.Sprintf("%v", validationErr.Value()),
            Parameter:     validationErr.Param(),
        })
    }

    return validierungsfehler
}

func postUser(ctx iris.Context) {
    var benutzer Benutzer
    err := ctx.ReadJSON(&benutzer)
    if err != nil {
        // Fehler behandeln, dies ist die richtige Vorgehensweise...

        if errs, ok := err.(validator.ValidationErrors); ok {
            // Fehler im JSON-Format umhüllen, die zugrunde liegende Bibliothek gibt Fehler vom Typ Schnittstelle zurück.
            validierungsfehler := wrapValidationErrors(errs)

            // Gibt eine Anwendungs-/json+problem-Antwort zurück und stoppt die Ausführung nachfolgender Handler
            ctx.StopWithProblem(iris.StatusBadRequest, iris.NewProblem().
                Title("Validierungsfehler").
                Detail("Ein oder mehrere Felder haben die Validierung nicht bestanden").
                Type("/benutzer/validierungsfehler").
                Key("errors", validierungsfehler))

            return
        }

        // Es könnte sich um einen internen JSON-Fehler handeln, hier werden keine weiteren Informationen bereitgestellt.
        ctx.StopWithStatus(iris.StatusInternalServerError)
        return
    }

    ctx.JSON(iris.Map{"message": "OK"})
}

func resolveErrorsDocumentation(ctx iris.Context) {
    ctx.WriteString("Diese Seite dient zur Erklärung, wie Validierungsfehler für Webentwickler oder API-Benutzer behoben werden können")
}
{
    "titel": "Validierungsfehler",
    "detail": "Eine oder mehrere Felder konnten nicht validiert werden",
    "type": "http://localhost:8080/benutzer/validierungsfehler",
    "status": 400,
    "fields": [
        {
            "tag": "erforderlich",
            "namespace": "Benutzer.Vorname",
            "kind": "string",
            "type": "string",
            "value": "",
            "param": ""
        },
        {
            "tag": "erforderlich",
            "namespace": "Benutzer.Nachname",
            "kind": "string",
            "type": "string",
            "value": "",
            "param": ""
        }
    ]
}

Binden von URL-Abfrageparametern

Die Methode ReadQuery bindet nur die Abfrageparameter, nicht die Daten des Anforderungskörpers. Verwenden Sie ReadForm, um die Daten des Anforderungskörpers zu binden.

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("Erfolg")
}

Binden beliebiger Daten

Binden Sie den Anforderungskörper an "ptr" basierend auf dem Inhaltstyp der vom Client gesendeten Daten, wie z.B. JSON, XML, YAML, MessagePack, Protobuf, Form und URL-Abfrage.

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("Erfolg")
}

Sie können mit dem folgenden Befehl testen:

$ curl -X GET "localhost:8085/testing?name=kataras&address=xyz&birthday=1992-03-15&createTime=1562400033000000123&unixTime=1562400033"

Binden von URL-Pfadparametern

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

Anfrage

$ curl -v http://localhost:8080/kataras/27/iris/web/framework

Binden von Header-Anforderungsparametern

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

Anfrage

curl -H "x-request-id:373713f0-6b4b-42ea-ab9f-e2e04bc38e73" -H "authentication: Bearer my-token" \
http://localhost:8080

Ergebnis

{
  "RequestID": "373713f0-6b4b-42ea-ab9f-e2e04bc38e73",
  "Authentication": "Bearer my-token"
}