Go in API planning directory and parameter validation

Posted by Dark_AngeL on Sat, 09 Nov 2019 21:34:01 +0100

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

Topics: Go JSON Database github