The introduction

  • Today we continue to refine the previous section
  • Using github.com/go-playground/validator/v10 for data verification, gin is very convenient
  • Pull business logic out of the Server layer (service layer)
  • Add interface signature verification (JWT custom middleware)
  • Github code address

1, add check

type userId struct {
	ID int `uri:"id" binding:"required"`
}

type user struct {
	Name      string `json:"name" xml:"name" form:"name" binding:"required"`
	CreatedBy string `json:"created_by" xml:"created_by" form:"created_by" binding:"lowercase"`
}

func GetUser(c *gin.Context) {
	user := new(userId)
	iferr := c.ShouldBindUri(user); err ! =nil {
		tool.JSONP(c, 40001, err.Error(), nil)
		return
	}
	res, err := models.GetUser(user.ID)
	iferr ! =nil {
		tool.JSONP(c, 40001."No data at present".nil)
		return
	}
	tool.JSONP(c, 0."Query successful", res)
}
Copy the code

Verify prompt Chinese culture

En. go was added to app/ Request

package request

import (
	"errors"

	"github.com/gin-gonic/gin/binding"
	zh "github.com/go-playground/locales/zh"
	ut "github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
	zh_translations "github.com/go-playground/validator/v10/translations/zh"
)

var v *validator.Validate
var trans ut.Translator

func init(a) {
	zh_ch := zh.New()
	uni := ut.New(zh_ch)
	trans, _ = uni.GetTranslator("zh")

	v, ok := binding.Validator.Engine().(*validator.Validate)
	if ok {
		// Validator registers translator
		zh_translations.RegisterDefaultTranslations(v, trans)
	}
}

func Translate(errs validator.ValidationErrors) string {
	var errList []string
	for _, e := range errs {
		// can translate each error one at a time.
		errList = append(errList, e.Translate(trans))

	}
	return errList[0] //strings.Join(errList, "|")
}

func GetError(err error) string {
	switch err.(type) {
	case validator.ValidationErrors:
		return Translate(err.(validator.ValidationErrors))
	default:
		return errors.New("unknown error.").Error()
	}

}

Copy the code

Custom error messages

Some validators defined by github.com/go-playground/validator are not translated (we can define ourselves)

V. registerTranslation ("lowercase", trans, func(ut ut.translator) error {return ut.Add("lowercase", "{0} must be lowercase ", true) // see universal-translator for details}, func(ut ut.translator, fe validator.FieldError) string { t, _ := ut.T("lowercase", fe.Field()) return t })Copy the code

Custom validators

Good interface extension, you can define some verification methods

v.RegisterValidation("checkMobile", checkMobile)

func checkMobile(fl validator.FieldLevel) bool {
	mobile := fl.Field().String()
	if len(mobile) ! =11 {
		return false
	}
	return true
}
Copy the code

2. Pull away from the service layer

By adding files under APP/Services, we defined interfaces at the service layer, defined standards, and ensured that if we replaced the service, it would not affect the controller layer

package services

import (
	"go-api/app/models"
	"go-api/tool"
)

type UserContract interface {
	UserGetsContract    //GET users
	UserGetContract     //GET user
	UserGetByIDContract //GETByID user
	UserAddContract     //Add user
	UserExistContract   //exist user
	UserDeleteContract  //delete user
	UserEditContract    //edit user
}

type UserGetsContract interface {
	GetUserList(maps interface{}, offset int, limit int) tool.M
}

type UserGetContract interface {
	GetUser(maps interface{}) tool.M
}

type UserGetByIDContract interface {
	GetUserByID(id int) tool.M
}

type UserAddContract interface {
	AddUser(maps map[string]interface{}, data map[string]interface{}) tool.M
}

type UserExistContract interface {
	ExistUser(maps map[string]interface{}) bool
}

type UserDeleteContract interface {
	DeleteUser(maps map[string]interface{}) tool.M
}

type UserEditContract interface {
	EditUser(id int, data interface{}) tool.M
}

type UserService struct{}func (T UserService) GetUserList(maps interface{}, offet int, limit int) tool.M {
	data := make(map[string]interface{})
	data["lists"] = models.GetUsers(offet, limit, maps)
	data["total"] = models.GetUserTotal(maps)
	return tool.DataReturn(true."Query successful", data)
}

func (T UserService) GetUser(maps interface{}) tool.M {
	data, err := models.GetUser(maps)
	iferr ! =nil {
		return tool.DataReturn(false."No data at present", err.Error())
	}
	return tool.DataReturn(true."Query successful", data)
}

func (T UserService) GetUserByID(id int) tool.M {
	data, err := models.GetUserByID(id)
	iferr ! =nil {
		return tool.DataReturn(false."No data at present", err.Error())
	}
	return tool.DataReturn(true."Query successful", data)
}

func (T UserService) AddUser(maps map[string]interface{}, data map[string]interface{}) tool.M {
	// Use MAPS to check if the data exists
	if T.ExistUser(maps) {
		return tool.DataReturn(false."The name already exists".nil)}else {
		ifisbool := models.AddUser(data); ! isbool {return tool.DataReturn(false."Create failed".nil)}return tool.DataReturn(true."Created successfully".nil)}}func (T UserService) ExistUser(maps map[string]interface{}) bool {

	return models.ExistUserByMaps(maps)
}

func (T UserService) DeleteUser(maps map[string]interface{}) tool.M {
	if T.ExistUser(maps) {
		isbool, err := models.DeleteUser(maps)
		iferr ! =nil {
			return tool.DataReturn(false."Delete failed", err.Error())
		}
		return tool.DataReturn(isbool, "Deleted successfully".nil)}else {
		return tool.DataReturn(false."Records do not exist.".nil)}}func (T UserService) EditUser(id int, data interface{}) tool.M {
	if models.ExistTagByID(id) {
		_, err := models.EditUser(id, data)
		iferr ! =nil {
			return tool.DataReturn(false."Edit failed", err.Error())
		}
		return tool.DataReturn(true."Edit successful".nil)}else {
		return tool.DataReturn(false."ID record does not exist".nil)}}Copy the code

2.1 Added the unified Service Layer return format

type M map[string]interface{}

type RetData struct{
	Status bool `json:"status"`
	Msg  string `json:"msg"`
	Data  interface{} `json:"data"`
}


func DataReturn(status bool,msg string,data interface{}) M{
	result :=M{
		"status" : status,
		"msg" : msg,
		"data" : data,
	}
	return result
}

func (m M) GetStatus(a) bool{
	return m["status"]. (bool)}func (m M) GetMsg(a) string{
	return m["msg"]. (string)}Copy the code

2.2 Modifying the Interface Controller Layer

var UserService services.UserContract

func init(a) {
	UserService = &services.UserService{}
}

func GetUser(c *gin.Context) {
	user := new(userId)
	iferr := c.ShouldBindUri(user); err ! =nil {
		tool.JSONP(c, 40001, request.GetError(err), nil)
		return
	}

	ret := UserService.GetUserByID(user.ID)

	if! ret.GetStatus() { tool.JSONP(c,40001, ret.GetMsg(), ret["data"])
		return
	}
	tool.JSONP(c, 0, ret.GetMsg(), ret["data"])}Copy the code

So far we have layered the interface (controller -> parameter verification -> service layer -> Model layer) and then added the interface signature verification

3. Interface signature verification

We use JWT to do interface verification and customize a middleware

3.1. Create the jwt.go tool

package tool

import (
	"time"

	jwt "github.com/dgrijalva/jwt-go"

	"go-api/config"
)

var (
	JwtSecret     = []byte(config.AppSetting.JwtSecret)
	JwtExpiresAt  = config.AppSetting.JwtExpiresAt
	SigningMethod = config.AppSetting.SigningMethod
)

type Claims struct {
	Appkey    string `json:"app_key"`
	AppSecret string `json:"app_secret"`
	jwt.StandardClaims
}

func GenerateToken(appkey, app_secret string) (string, error) {
	nowTime := time.Now()
	expireTime := nowTime.Add(JwtExpiresAt)

	claims := Claims{
		appkey,     // Consider encryption
		app_secret, // Consider encryption
		jwt.StandardClaims{
			ExpiresAt: expireTime.Unix(),
			Issuer:    "go-api",
		},
	}

	tokenClaims := jwt.NewWithClaims(jwt.GetSigningMethod(SigningMethod), claims)
	token, err := tokenClaims.SignedString(JwtSecret)

	return token, err
}

func ParseToken(token string) (*Claims, error) {
	tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		return JwtSecret, nil
	})

	iftokenClaims ! =nil {
		if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid {
			return claims, nil}}return nil, err
}

Copy the code

3.2. Custom middleware

In the app/middleware


package middleware

import (
	"net/http"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"

	"go-api/tool"
)

func JWT(a) gin.HandlerFunc {
	return func(c *gin.Context) {
		var (
			code  = tool.SUCCESS
			token string
		)
		// Supports header and GET
		if s, ok := c.GetQuery("token"); ok {
			token = s
		} else {
			token = c.GetHeader("token")}if token == "" {
			code = tool.ERROR_AUTH_CHECK_TOKEN_EMPTY
		} else {
			_, err := tool.ParseToken(token)
			iferr ! =nil {
				switch err.(*jwt.ValidationError).Errors {
				case jwt.ValidationErrorExpired:
					code = tool.ERROR_AUTH_CHECK_TOKEN_TIMEOUT
				default:
					code = tool.ERROR_AUTH_CHECK_TOKEN_FAIL
				}
			}
		}

		ifcode ! = tool.SUCCESS { c.JSON(http.StatusUnauthorized, gin.H{"code": code,
				"msg":  tool.GetMsg(code),
				"data": nil,
			})

			c.Abort()
			return} c.ext ()}} ·· ###3.2Routing groups apply JWT middleware` ``golang package routes import ( "go-api/app/controller" "go-api/app/middleware" "go-api/tool" "github.com/gin-gonic/gin"  ) func apiRoute(r *gin.Engine) { r.GET("/auth/:key", func(c *gin.Context) { appkey := c.Param("key") token, err := tool.GenerateToken(appkey, "12312323") if err ! = nil {tool.JSONP(c, tool.ERROR_AUTH_TOKEN, "", nil) return} tool.JSONP(c, 0, "success ", Token)}) apiv1 := r.group ("/ API /v1", middleware.jwt ()) {// GET("/users", Controller.getusers) // GET user apiv1.GET("/user/: ID ", controller.getuser) // Add user apiv1.POST("/users", Controller.adduser) // Update user apiv1.PUT("/users/:id", controller.edituser) // DELETE user apiv1.DELETE("/users/:id", controller.DeleteUser) } }Copy the code

Finally, compile the code for validation or unit testing

4. Series of articles

  • Serialized a Golang environment build
  • Serial II installation Gin
  • Serial three defines the directory structure
  • Serial four builds case API1
  • Serial five builds case API2
  • Serial six access Swagger interface document
  • Serial seven Log components
  • Serial eight gracefully restarts and stops
  • Serial Makefile build
  • Serial Cron timing mission
  • Serial build command line tools
  • Create a dedicated Cache(First Day)
  • Create your own Cache in 3 days (Second Day)
  • Create a dedicated Cache(Third Day)