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.
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.
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
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.
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}