بخش‌های قبلی روش خواندن مستقیم پارامترهای درخواست را معرفی کرد. اگر خواندن هر پارامتر به طور جداگانه برایتان سخت است، فریم‌ورک iris همچنین یک مکانیزم بایندینگ پارامترها ارائه می‌دهد که می‌تواند پارامترهای درخواست را به یک ساختار (struct) ببندد و همچنین یک مکانیزم اعتبارسنجی پارامتر فرم را نیز پشتیبانی می‌کند.

مدل بایندینگ و اعتبارسنجی

برای اتصال بدنه درخواست به یک نوع، از مدل بایندینگ استفاده کنید. در حال حاضر، ما پشتیبانی می‌کنیم از انواع بایندینگ مانند JSON، JSONProtobuf، Protobuf، MsgPack، XML، YAML و مقادیر استاندارد فرم (foo=bar&boo=baz).

// زیرا وظایف توابع برای بایند کردن پارامترهای درخواست به یک ساختار داده
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

هنگام استفاده از ReadBody، ایریس با توجه به هدر Content-Type، بایندر را استخراج می‌کند. اگر مطمئن هستید درباره محتوا که می‌خواهید بایند کنید، می‌توانید از متدهای خاص ReadXXX مانند ReadJSON یا ReadProtobuf استفاده کنید.

ReadBody(ptr interface{}) error

ایریس با اعتبارسنجی داده هوشمند داخلی تامین می‌کند. با این حال، این امکان را به شما می‌دهد که یک اعتبارسنج را متصل کنید که به طور خودکار در متدهایی مانند ReadJSON و ReadXML فراخوانی می‌شود. در این مثال، ما یاد خواهیم گرفت چگونه از go-playground/validator/v10 برای اعتبارسنجی بدنه درخواست استفاده کنیم.

لطفاً توجه داشته باشید که شما باید برچسب‌های بایندینگ مربوط را بر روی تمام فیلدها برای بایند کردن قرار دهید. به عنوان مثال، هنگام بایندینگ از JSON، json:"fieldname" را تنظیم کنید.

همچنین می‌توانید فیلدهای خاصی را به عنوان فیلدهای مورد نیاز مشخص کنید. اگر یک فیلد تزئین شده با binding:"required" باشد و هیچ مقداری در هنگام بایند کردن فراهم نشود، یک خطا برگردانده خواهد شد.

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("/کاربر")
    {
        userRouter.Get("/errors-documentation", resolveErrorsDocumentation)
        userRouter.Post("/", postUser)
    }
    app.Listen(":8080")
}

// User contains user information.
type User struct {
    FirstName      string     `json:"نام" validate:"required"` // First name, required
    LastName       string     `json:"نام_خانوادگی" validate:"required"` // Last name, required
    Age            uint8      `json:"سن" validate:"gte=0,lte=130"` // Age, range between 0 and 130
    Email          string     `json:"ایمیل" validate:"required,email"` // Email, required
    FavouriteColor string     `json:"رنگ_مورد_علاقه" validate:"hexcolor|rgb|rgba"` // Favorite color, must be a legal hexadecimal, RGB, or RGBA color value
    Addresses      []*Address `json:"آدرسها" validate:"required,dive,required"` // Address list, must not be empty and each address item is required
}

// Address stores user address information.
type Address struct {
    Street string `json:"خیابان" validate:"required"` // Street, required
    City   string `json:"شهر" validate:"required"` // City, required
    Planet string `json:"سیاره" validate:"required"` // Planet, required
    Phone  string `json:"تلفن" validate:"required"` // Phone, required
}

type validationError struct {
    ActualTag string `json:"tag"` // Actual tag
    Namespace string `json:"namespace"` // Namespace
    Kind      string `json:"kind"` // Kind
    Type      string `json:"type"` // Type
    Value     string `json:"value"` // Value
    Param     string `json:"param"` // Parameter
}

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 {
        // Handle errors, the following is the correct way...

        if errs, ok := err.(validator.ValidationErrors); ok {
            // Wrap errors in JSON format, the underlying library returns errors of type interface.
            validationErrors := wrapValidationErrors(errs)

            // Return an application/json+problem response and stop executing subsequent handlers
            ctx.StopWithProblem(iris.StatusBadRequest, iris.NewProblem().
                Title("Validation Errors").
                Detail("One or more fields did not pass validation").
                Type("/کاربر/خطاهای-اعتبارسنجی").
                Key("errors", validationErrors))

            return
        }

        // It might be an internal JSON error, no further information provided here.
        ctx.StopWithStatus(iris.StatusInternalServerError)
        return
    }

    ctx.JSON(iris.Map{"پیام": "با موفقیت"})
}

func resolveErrorsDocumentation(ctx iris.Context) {
    ctx.WriteString("این صفحه برای توضیح نحوه رفع خطاهای اعتبارسنجی برای توسعه دهندگان و کاربران API استفاده می‌شود")
}

درخواست نمونه

{
    "fname": "",
    "lname": "",
    "age": 45,
    "email": "[email protected]",
    "favColor": "#000",
    "addresses": [{
        "street": "Eavesdown Docks",
        "planet": "Persphone",
        "phone": "none",
        "city": "نامشخص"
    }]
}

پاسخ نمونه

{
    "title": "خطا در اعتبارسنجی",
    "detail": "یک یا چند فیلد با موفقیت اعتبارسنجی نشده‌اند",
    "type": "http://localhost:8080/user/validation-errors",
    "status": 400,
    "fields": [
        {
            "tag": "required",
            "namespace": "User.FirstName",
            "kind": "string",
            "type": "string",
            "value": "",
            "param": ""
        },
        {
            "tag": "required",
            "namespace": "User.LastName",
            "kind": "string",
            "type": "string",
            "value": "",
            "param": ""
        }
    ]
}

پیوند پارامترهای URL درخواست

متد ReadQuery تنها پارامترهای پرس و جو را پیوند می‌کند و داده‌های بدنه درخواست را پیوند نمی‌کند. برای پیوند داده‌های بدنه از ReadForm استفاده کنید.

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("موفقیت")
}

پیوند داده‌های دلخواه

درخواست بدنه را بر اساس نوع محتوای داده‌ای ارسالی توسط مشتری به "ptr" پیوند کنید، مانند JSON، XML، YAML، MessagePack، Protobuf، Form، و پارامترهای 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("موفقیت")
}

می‌توانید با دستور زیر تست کنید:

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

پیوند پارامترهای مسیر 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")
}

درخواست

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

پیوند پارامترهای درخواست سرآیند

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

درخواست

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

پاسخ

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