前面章节介绍了直接读取请求参数的方式,如果所有的参数都一个个读取比较麻烦,iris框架也提供了参数绑定机制,可以将请求参数绑定到struct结构体中,同时也支持表单参数校验机制。
模型绑定和验证
要将请求体绑定到类型中,请使用模型绑定。我们目前支持绑定的类型有JSON
、JSONProtobuf
、Protobuf
、MsgPack
、XML
、YAML
和标准表单值(foo=bar&boo=baz)。
// 下面是各种格式的请求参数绑定到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
当使用ReadBody
时,Iris会根据Content-Type头来推断绑定器。如果你确定你要绑定的内容,可以使用特定的ReadXXX
方法,例如ReadJSON
或ReadProtobuf
等。
ReadBody(ptr interface{}) error
Iris具有智能的内置数据验证功能。但是,它允许您附加一个验证器,该验证器将自动在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("/user")
{
userRouter.Get("/validation-errors", resolveErrorsDocumentation)
userRouter.Post("/", postUser)
}
app.Listen(":8080")
}
// User包含用户信息。
type User struct {
FirstName string `json:"fname" validate:"required"` // 用户名,必填
LastName string `json:"lname" validate:"required"` // 姓氏,必填
Age uint8 `json:"age" validate:"gte=0,lte=130"` // 年龄,范围在0到130之间
Email string `json:"email" validate:"required,email"` // 邮箱,必填
FavouriteColor string `json:"favColor" validate:"hexcolor|rgb|rgba"` // 最喜欢的颜色,必须是合法的十六进制、RGB或RGBA颜色值
Addresses []*Address `json:"addresses" validate:"required,dive,required"` // 地址列表,不能为空且每个地址项都必填
}
// Address存储用户地址信息。
type Address struct {
Street string `json:"street" validate:"required"` // 街道,必填
City string `json:"city" validate:"required"` // 城市,必填
Planet string `json:"planet" validate:"required"` // 行星,必填
Phone string `json:"phone" validate:"required"` // 电话,必填
}
type validationError struct {
ActualTag string `json:"tag"` // 实际标签
Namespace string `json:"namespace"` // 命名空间
Kind string `json:"kind"` // 类型
Type string `json:"type"` // 类别
Value string `json:"value"` // 值
Param string `json:"param"` // 参数
}
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 {
// 处理错误,下面是正确的处理方式…
if errs, ok := err.(validator.ValidationErrors); ok {
// 使用JSON格式封装错误,底层库返回的错误为interface类型。
validationErrors := wrapValidationErrors(errs)
// 返回一个application/json+problem响应并停止执行后续处理器
ctx.StopWithProblem(iris.StatusBadRequest, iris.NewProblem().
Title("验证错误").
Detail("一个或多个字段未通过验证").
Type("/user/validation-errors").
Key("errors", validationErrors))
return
}
// 可能是内部JSON错误,不在此处提供更多信息。
ctx.StopWithStatus(iris.StatusInternalServerError)
return
}
ctx.JSON(iris.Map{"message": "OK"})
}
func resolveErrorsDocumentation(ctx iris.Context) {
ctx.WriteString("此页用于向Web开发人员或API用户说明如何解决验证错误")
}
示例请求
{
"fname": "",
"lname": "",
"age": 45,
"email": "mail@example.com",
"favColor": "#000",
"addresses": [{
"street": "Eavesdown Docks",
"planet": "Persphone",
"phone": "none",
"city": "Unknown"
}]
}
示例响应
{
"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 query参数
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("Success")
}
绑定任意数据
根据客户端发送的数据的内容类型,将请求体绑定到 “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("Success")
}
可以使用以下命令进行测试:
$ 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
绑定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")
}
请求
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"
}