[Series] go-gin-api planning catalogue and parameter verification

Keywords: Go JSON Database github

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(&param_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(&param_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

go-gin-api series of articles

Posted by WhiteHawksan on Tue, 27 Aug 2019 22:45:58 -0700