The introduction

  • What does a complete API service consist of?
  • Routing, controller layer, Model layer, unified return format
  • So let’s implement each of these
  • Github code address

1. Configure routes first

config/api.go

package routes

import (
	"go-api/app/controller"

	"github.com/gin-gonic/gin"
)

func apiRoute(r *gin.Engine){
	apiv1 := r.Group("/api/v1")
	{
		// Get the user list
		apiv1.GET("/users", controller.GetUsers)
		// Get the specified user
		apiv1.GET("/user/:id", controller.GetUser)
        // Add a user
        apiv1.POST("/users", controller.AddUser)
        // Update the specified user
        apiv1.PUT("/users/:id", controller.EditUser)
        // Delete the specified user
		apiv1.DELETE("/users/:id", controller.DeleteUser)
	}
}
Copy the code

2. In the tool directory

New convent. Go

package tool

import (
	"encoding/json"
	"reflect"
	"strconv"
	//"fmt"
)

func JsonToStruct(data []byte, s interface{}) error {
	err := json.Unmarshal(data, s)
	iferr ! =nil {
		//err = FMT.Sprintf("Json marshaling failed: %s", err)
		return err
	}

	return nil
}

func StringToInt(str string) int {
	variable, _ := strconv.Atoi(str)
	return variable
}

func StructToMap(obj interface{}) map[string]interface{} {

	t := reflect.TypeOf(obj)
	v := reflect.ValueOf(obj)

	var data = make(map[string]interface{})
	for i := 0; i < t.NumField(); i++ {
		data[t.Field(i).Name] = v.Field(i).Interface()
	}
	return data
}

Copy the code

Added pagination. Go (handle global fetching page start offset)

package tool

import (
	"github.com/gin-gonic/gin"

	"go-api/config"
)

//get Offset limit Optional
func GetOffset(c *gin.Context, limit int) int {
	page := StringToInt(c.DefaultQuery("page"."0"))
	return getOffset(page, limit)
}

//get Offset limit default
func DefaultGetOffset(c *gin.Context) int {
	page := StringToInt(c.DefaultQuery("page"."0"))
	return getOffset(page, config.AppSetting.PageSize)
}

func getOffset(page int, limit int) int {
	result := 0
	if page > 0 {
		result = (page - 1) * limit
	}
	return result
}

Copy the code

Added apIMsg. go (API return error code corresponding error message)

package tool

import "fmt"

const (
	SUCCESS        = 0
	ERROR          = 500
	INVALID_PARAMS = 400
	CUSTOM_ERROR   = 40001

	ERROR_AUTH_CHECK_TOKEN_FAIL    = 20001
	ERROR_AUTH_CHECK_TOKEN_TIMEOUT = 20002
	ERROR_AUTH_TOKEN               = 20003
	ERROR_AUTH                     = 20004
	ERROR_AUTH_CHECK_TOKEN_EMPTY   = 20005
)

var MsgFlags = map[int]string{
	SUCCESS:                        "%s",
	ERROR:                          "%s",
	INVALID_PARAMS:                 "Error with parameter %s",
	CUSTOM_ERROR:                   "%s",
	ERROR_AUTH_CHECK_TOKEN_FAIL:    "Token authentication failed",
	ERROR_AUTH_CHECK_TOKEN_TIMEOUT: "Token has timed out",
	ERROR_AUTH_TOKEN:               "Token generation failed",
	ERROR_AUTH:                     "Token error",
	ERROR_AUTH_CHECK_TOKEN_EMPTY:   "Token parameter cannot be empty",}func GetMsg(code int, Msg ...interface{}) string {
	msg, ok := MsgFlags[code]
	if ok {
		return fmt.Sprintf(msg, Msg...)
	}

	return MsgFlags[ERROR]
}

Copy the code

3. Improve models

Perfect models/user. Go

package models

type User struct {
	Model

	Name       string `json:"name"`
	CreatedBy  string `json:"created_by"`
	ModifiedBy string `json:"modified_by"`
}

func GetUsers(pageNum int, pageSize int, maps interface{}) (users []User) {
	db.Where(maps).Offset(pageNum).Limit(pageSize).Find(&users)

	return
}

func GetUser(id int) (user User, err error) {
	iferr := db.First(&user, id).Error; err ! =nil {
		return user, err
	}
	return user, nil
}

func GetUserTotal(maps interface{}) (count int) {
	db.Model(&User{}).Where(maps).Count(&count)

	return
}

func ExistUserByMaps(maps interface{}) bool {
	var user User
	db.Select("id").Where(maps).First(&user)
	if user.ID > 0 {
		return true
	}

	return false
}

func AddUser(Users map[string]interface{}) bool {
	user := User{
		Name:      Users["Name"]. (string),
		CreatedBy: Users["CreatedBy"]. (string),
	}
	db.Create(&user)
	return! db.NewRecord(user) }func ExistTagByID(id int) bool {
	var user User
	db.Select("id").Where("id = ?", id).First(&user)
	if user.ID > 0 {
		return true
	}

	return false
}

func DeleteUser(maps interface{}) (bool, error) {
	iferr := db.Where(maps).Delete(&User{}).Error; err ! =nil {
		return false, err
	}
	return true.nil
}

func EditUser(id int, data interface{}) (bool, error) {
	if err := db.Model(&User{}).Where("id = ?", id).Updates(data).Error; err ! =nil {
		return false, err
	}
	return true.nil
}

Copy the code

4. Improve the controller

Improve the controller/user. Go

package controller

import (
	"github.com/gin-gonic/gin"

	"fmt"
	"go-api/app/models"
	"go-api/tool"
)

func GetUsers(c *gin.Context) {
	maps := make(map[string]interface{})
	data := make(map[string]interface{})
	data["total"] = models.GetUserTotal(maps)
	data["list"] = models.GetUsers(tool.DefaultGetOffset(c), 10, maps)
	c.JSONP(200, gin.H{"error_code": 0."msg": tool.GetMsg(0."Query successful"), "data": data})
}

func GetUser(c *gin.Context) {
	id := c.Param("id")

	res, err := models.GetUser(tool.StringToInt(id))
	iferr ! =nil {
		c.JSONP(200, gin.H{"error_code": 40001."msg": tool.GetMsg(40001."No data at present"), "err": fmt.Sprint(err)})
	} else {
		c.JSONP(200, gin.H{"error_code": 0."msg": tool.GetMsg(0."Query successful"), "data": res})
	}
}

func AddUser(c *gin.Context) {
	name := c.PostForm("name")
	created_by := c.PostForm("created_by")
	data := make(map[string]interface{})
	maps := make(map[string]interface{})
	if name == "" {
		c.JSONP(200, gin.H{"error_code": 40001."msg": tool.GetMsg(40001."Name cannot be empty")})}else {
		data["Name"] = name
		maps["Name"] = name
		data["CreatedBy"] = created_by

		if! models.ExistUserByMaps(maps) { res := models.AddUser(data)if res {
				c.JSONP(200, gin.H{"error_code": 0."msg": tool.GetMsg(0."Created successfully"), "data": data})
			} else {
				c.JSONP(200, gin.H{"error_code": 40001."msg": tool.GetMsg(40001."Create failed")}}}else {
			c.JSONP(200, gin.H{"error_code": 40001."msg": tool.GetMsg(40001."The name already exists"), "map": maps})
		}
	}

}

func EditUser(c *gin.Context) {
	id := tool.StringToInt(c.Param("id"))
	name := c.PostForm("name")
	created_by := c.PostForm("created_by")

	if! models.ExistTagByID(id) { c.JSONP(200, gin.H{
			"error_code": 40001."msg":        tool.GetMsg(40001.ID does not exist),})}else {
		data := make(map[string]interface{})
		data["name"] = name
		data["created_by"] = created_by
		res, err := models.EditUser(id, data)
		if res {
			c.JSONP(200, gin.H{
				"error_code": 0."msg":        tool.GetMsg(40001."Edit successful"),})}else {
			c.JSONP(200, gin.H{
				"error_code": 40001."msg":        tool.GetMsg(40001."Edit failed"),
				"err":        err,
			})
		}
	}
}

func DeleteUser(c *gin.Context) {
	id := tool.StringToInt(c.Param("id"))

	if id <= 0 {
		c.JSONP(200, gin.H{
			"error_code": 40001."msg":        tool.GetMsg(40001.ID does not exist),})}else {

		if models.ExistTagByID(id) {
			maps := make(map[string]interface{})

			maps["id"] = id
			res, err := models.DeleteUser(maps)
			if res {
				c.JSONP(200, gin.H{
					"error_code": 0."msg":        tool.GetMsg(40001."Deleted successfully"),
					"err":        err,
				})
			} else {
				c.JSONP(200, gin.H{
					"error_code": 40001."msg":        tool.GetMsg(40001."Delete failed"),
					"err":        err,
				})
			}
		} else {
			c.JSONP(200, gin.H{
				"error_code": 40001."msg":        tool.GetMsg(40001."Information does not exist."),})}}}Copy the code

The main main. Go

package main

import (
    "go-api/routes"
)

func main(a) {
    // Start the server
    routes.Run()
}
Copy the code

Start and verify

In this section we focus on the interaction between the general API and the Model layer, as well as unifying returns and error formats

The next section uses gin default data verification at the controller layer, pulls out the service layer, and adds JWT middleware to verify API service permissions

5. 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)