Catalog
Summary
Firstly, the project overview is synchronized:
As we shared in the last article, we used go modules to initialize projects. Let's share this article:
- Planning catalogue structure
- Model binding and validation
- Custom Verifier
- Develop API return structure
No more nonsense. Let's get started.
Planning catalogue structure
├─ go-gin-api │ ├─ app │ ├─ config //configuration file │ ├─ config.go │ ├─ controller //Controller Layer │ ├─ param_bind │ ├─ param_verify │ ├─ ... │ ├─ model //Database ORM │ ├─ proto │ ├─ ... │ ├─ repository //Database Operating Layer │ ├─ ... │ ├─ route //Route │ ├─ middleware │ ├─ route.go │ ├─ service //Business layer │ ├─ ... │ ├─ util //Tool kit │ ├─ ... │ ├─ vendor //Dependency packages │ ├─ ... │ ├─ go.mod │ ├─ go.sum │ ├─ main.go //Entry file
The above directory structure is customized by me, and you can also define it according to your own habits.
The controller layer mainly verifies the submitted data, and then transfers the validated data to service processing.
In gin framework, there are two kinds of parameter validation:
1. Model binding and validation.
2. Custom Verifier.
The directory param_bind stores parameter-bound data, and the directory param_verify stores custom validators.
Next, let's have a simple implementation.
Model binding and validation
For example, there is an interface to create a commodity, and the name of the commodity cannot be empty.
Configuration routing (route.go):
ProductRouter := engine.Group("") { // New products ProductRouter.POST("/product", product.Add) // Update products ProductRouter.PUT("/product/:id", product.Edit) // Delete products ProductRouter.DELETE("/product/:id", product.Delete) // Get product details ProductRouter.GET("/product/:id", product.Detail) }
Parametric binding (param_bind/product.go):
type ProductAdd struct { Name string `form:"name" json:"name" binding:"required"` }
Controller call (controller/product.go):
if err := c.ShouldBind(¶m_bind.ProductAdd{}); err != nil { utilGin.Response(-1, err.Error(), nil) return }
When we use Postman to simulate a post request, the name parameter does not pass or is passed empty, and it appears:
Key: 'ProductAdd.Name' Error:Field validation for 'Name' failed on the 'required' tag
This uses binding:"required" for parameter settings.
What other parameters can binding be used? Does it have documentation?
Yes. Gin uses go-playground/validator.v8 for validation, with relevant documentation:
https://godoc.org/gopkg.in/go-playground/validator.v8
Next, let's implement a custom validator.
Custom Verifier
For example, there is an interface to create a commodity, the commodity name cannot be empty and the parameter name cannot be equal to admin.
Similar to this business requirement, there is no ready-made binding method, we need to write our own verification method to achieve.
Custom Validation Method (param_verify/product.go)
func NameValid ( v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string, ) bool { if s, ok := field.Interface().(string); ok { if s == "admin" { return false } } return true }
Parametric binding (param_bind/product.go):
type ProductAdd struct { Name string `form:"name" json:"name" binding:"required,NameValid"` }
Also bind the validator:
// Binding Verifier if v, ok := binding.Validator.Engine().(*validator.Validate); ok { v.RegisterValidation("NameValid", param_verify.NameValid) }
When we use Postman to simulate a post request, the name parameter does not pass or is passed empty, and it appears:
Key: 'ProductAdd.Name' Error:Field validation for 'Name' failed on the 'required' tag
When name=admin:
Key: 'ProductAdd.Name' Error:Field validation for 'Name' failed on the 'NameValid' tag
OK, the above two validations are in effect!
The above output is in the console, can you return a Json structure data?
Yes. Next, let's work out the API return structure.
Develop API return structure
{ "code": 1, "msg": "", "data": null }
The structure returned by API interface is basically these three fields.
For example, code=1 denotes success and code=-1 denotes failure.
msg represents prompt information.
Data represents the data to be returned.
So, how can we implement it in gin framework? In fact, it's very simple to encapsulate it based on c.JSON() method. Just look at the code directly.
package util import "github.com/gin-gonic/gin" type Gin struct { Ctx *gin.Context } type response struct { Code int `json:"code"` Message string `json:"msg"` Data interface{} `json:"data"` } func (g *Gin)Response(code int, msg string, data interface{}) { g.Ctx.JSON(200, response{ Code : code, Message : msg, Data : data, }) return }
Controller call (controller/product.go):
utilGin := util.Gin{Ctx:c} if err := c.ShouldBind(¶m_bind.ProductAdd{}); err != nil { utilGin.Response(-1, err.Error(), nil) return }
When we use Postman to simulate a post request, the name parameter does not pass or is passed empty, and it appears:
{ "code": -1, "msg": "Key: 'ProductAdd.Name' Error:Field validation for 'Name' failed on the 'required' tag", "data": null }
When name=admin:
{ "code": -1, "msg": "Key: 'ProductAdd.Name' Error:Field validation for 'Name' failed on the 'NameValid' tag", "data": null }
OK, the above two validations are in effect!
Source address
https://github.com/xinliangnote/go-gin-api