Skip to main content
Version: 2.8.x(Latest)

The Request object offers excellent request validation capabilities by binding the v tag to struct attributes. Since the underlying validation functionality is achieved through the gvalid module, for more detailed validation rules and introduction, please refer to the Struct Validation - Example section.

warning

The way of transforming request parameters to structs in the example code below applies to framework v1. From version v2, it is recommended to implement automated parameter struct transformation and validation through standard routes: Standard Router

Example 1, Basic Usage

We will adjust the previous example to add the v validation tag.

package main

import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)

// RegisterReq Register request data structure
type RegisterReq struct {
Name string `p:"username" v:"required|length:4,30#Please enter an account|Account length should be between {min} and {max} characters"`
Pass string `p:"password1" v:"required|length:6,30#Please enter the password|Password length is insufficient"`
Pass2 string `p:"password2" v:"required|length:6,30|same:password1#Please confirm the password|Password length is insufficient|Passwords do not match"`
}

// RegisterRes Register response data structure
type RegisterRes struct {
Code int `json:"code"`
Error string `json:"error"`
Data interface{} `json:"data"`
}

func main() {
s := g.Server()
s.Group("/", func(group *ghttp.RouterGroup) {
group.ALL("/register", func(r *ghttp.Request) {
var req *RegisterReq
if err := r.Parse(&req); err != nil {
r.Response.WriteJsonExit(RegisterRes{
Code: 1,
Error: err.Error(),
})
}
// ...
r.Response.WriteJsonExit(RegisterRes{
Data: req,
})
})
})
s.SetPort(8199)
s.Run()
}

In this example, we define two structs: RegisterReq for receiving parameters and RegisterRes for returning data. Since the API returns a JSON data structure, you can see that only the returned struct has the json tag, while the receiving struct only has the p tag. This is because RegisterReq is only used for receiving parameters and does not need to set the returned json tag.

tip

The p tag is optional; by default, property name matching and conversion are performed under the ignore special characters and case-insensitive rules, meeting most business scenarios' needs by default.

For demonstration purposes, the RegisterReq object is returned in the normal return result Data property. Since this object does not have a bound json tag, the returned JSON fields will match its property names.

After executing, we use the curl tool to test:

$ curl "http://127.0.0.1:8199/register?name=john&password1=123456&password2=123456"
{"code":0,"error":"","data":{"Name":"john","Pass":"123456","Pass2":"123456"}}

$ curl "http://127.0.0.1:8199/register?name=john&password1=123456&password2=12345"
{"code":1,"error":"Password length is insufficient","data":null}

$ curl "http://127.0.0.1:8199/register"
{"code":1,"error":"Please enter an account","data":null}

Example 2, Validation Error Handling

tip

In the latest version, only the first error is returned.

As shown in the above example, when a request validation error occurs, all validation failure errors are returned, which may not be very user-friendly. When an error occurs, we can convert the validation error into a gvalid.Error API object and then control the error return flexibly.

package main

import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/util/gvalid"
)

type RegisterReq struct {
Name string `p:"username" v:"required|length:4,30#Please enter an account|Account length should be between {min} and {max} characters"`
Pass string `p:"password1" v:"required|length:6,30#Please enter the password|Password length is insufficient"`
Pass2 string `p:"password2" v:"required|length:6,30|same:password1#Please confirm the password|Password length is insufficient|Passwords do not match"`
}

type RegisterRes struct {
Code int `json:"code"`
Error string `json:"error"`
Data interface{} `json:"data"`
}

func main() {
s := g.Server()
s.Group("/", func(group *ghttp.RouterGroup) {
group.ALL("/register", func(r *ghttp.Request) {
var req *RegisterReq
if err := r.Parse(&req); err != nil {
// Validation error.
if v, ok := err.(gvalid.Error); ok {
r.Response.WriteJsonExit(RegisterRes{
Code: 1,
Error: v.FirstError().Error(),
})
}
// Other error.
r.Response.WriteJsonExit(RegisterRes{
Code: 1,
Error: err.Error(),
})
}
// ...
r.Response.WriteJsonExit(RegisterRes{
Data: req,
})
})
})
s.SetPort(8199)
s.Run()
}

As shown, when an error occurs, we can use the err.(gvalid.Error) assertion method to determine whether the error is a validation error. If so, the first validation error is returned instead of all. For more detailed error control methods, please refer to the Data Validation - Result section.

tip

Moreover, we can use gerror.Current to obtain the first error message instead of using the assertion judgment. For example:

var req *RegisterReq
if err := r.Parse(&req); err != nil {
r.Response.WriteJsonExit(RegisterRes{
Code: 1,
Error: gerror.Current(err).Error(),
})
}

After executing, we test using the curl tool:

$ curl "http://127.0.0.1:8199/register"
{"code":1,"error":"Please enter an account","data":null}

$ curl "http://127.0.0.1:8199/register?name=john&password1=123456&password2=1234567"
{"code":1,"error":"Passwords do not match","data":null}