Summary
First, synchronize the following project overview:
In the previous article, we used go modules to initialize the project. In this article, we share:
Planning directory structure
Model binding and validation
Custom validator
Make API return structure
Don't talk too much. Let's start.
Planning directory structure
├─ go-gin-api │ ├─ app │ ├─ config //configuration file │ ├─ config.go │ ├─ controller //Controller layer │ ├─ param_bind │ ├─ param_verify │ ├─ ... │ ├─ model //Database ORM │ ├─ proto │ ├─ ... │ ├─ repository //Database operation layer │ ├─ ... │ ├─ route //Route │ ├─ middleware │ ├─ route.go │ ├─ service //Business layer │ ├─ ... │ ├─ util //Tool kit │ ├─ ... │ ├─ vendor //Dependency package │ ├─ ... │ ├─ go.mod │ ├─ go.sum │ ├─ main.go //Entry file
The directory structure above is customized by me. You can also define it according to your own habits.
The controller layer mainly validates the submitted data, and then passes the verified data to the service for processing.
In the gin framework, there are two types of parameter validation:
1. Model binding and validation.
2. Custom verifier.
Among them, the directory param? Bind stores data bound by parameters, and the directory param? Verify stores custom validators.
Next, let's do a simple implementation.
Model binding and validation
For example, there is an interface to create a product, and the product name cannot be empty.
Configure route (route.go):
ProductRouter := engine.Group("") { // New product ProductRouter.POST("/product", product.Add) // Update product ProductRouter.PUT("/product/:id", product.Edit) // Delete product ProductRouter.DELETE("/product/:id", product.Delete) // Get product details ProductRouter.GET("/product/:id", product.Detail) }
Parameter 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 post request, if the name parameter is not passed or is empty, it will appear:
Key: 'ProductAdd.Name' Error:Field validation for 'Name' failed on the 'required' tag
This indicates that the binding of parameter setting is used: "required".
What other binding parameters can be used? Is there any document?
Yes. Gin uses go-playground/validator.v8 for validation. Related documents:
https://godoc.org/gopkg.in/go-playground/validator.v8
Next, let's implement a custom validator.
Custom validator
For example, there is an interface to create a product. The product name cannot be empty and the parameter name cannot be equal to admin.
Similar to this kind of business requirements, we can't bind existing methods. We need to write our own verification methods to achieve this.
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 }
Parameter binding (param [bind / product. Go):
type ProductAdd struct { Name string `form:"name" json:"name" binding:"required,NameValid"` }
Also bind the verifier:
// Binding validator if v, ok := binding.Validator.Engine().(*validator.Validate); ok { v.RegisterValidation("NameValid", param_verify.NameValid) }
When we use Postman to simulate post request, if the name parameter is not passed or is empty, it will appear:
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 have taken 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 return structure of API interface is basically these three fields.
For example, code=1 means success, code=-1 means failure.
msg means prompt message.
Data represents the returned data.
So, how do we implement it in the gin framework?
In fact, it's very simple to encapsulate based on the 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 post request, if the name parameter is not passed or is empty, it will appear:
{ "code": -1, "msg": "Key: 'ProductAdd.Name' Error:Field validation for 'Name' failed on the 'required' tag", "data": null }
name=admin Time: { "code": -1, "msg": "Key: 'ProductAdd.Name' Error:Field validation for 'Name' failed on the 'NameValid' tag", "data": null }
OK, the above two validations have taken effect!