gin框架实践连载四 | 搭建一个案例API1

2,388 阅读4分钟

引言

  • 一个完整的api服务包括哪些?
  • 路由、控制器层、model层、统一返回格式
  • 接下来我们一一实现这些
  • github代码地址

1、首先路由配置

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")
	{
		//获取用户列表
		apiv1.GET("/users", controller.GetUsers)
		//获取指定用户
		apiv1.GET("/user/:id", controller.GetUser)
        //新增用户
        apiv1.POST("/users", controller.AddUser)
        //更新指定用户
        apiv1.PUT("/users/:id", controller.EditUser)
        //删除指定用户
		apiv1.DELETE("/users/:id", controller.DeleteUser)
	}
}

2、在tool目录下

新增convent.go

package tool

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

func JsonToStruct(data []byte, s interface{}) error {
	err := json.Unmarshal(data, s)
	if err != 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
}

新增pagination.go (处理全局获取page分页 起始偏移量)

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
}

新增apiMsg.go (API返回错误码对应错误信息)

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:                 "参数%s错误",
	CUSTOM_ERROR:                   "%s",
	ERROR_AUTH_CHECK_TOKEN_FAIL:    "Token鉴权失败",
	ERROR_AUTH_CHECK_TOKEN_TIMEOUT: "Token已超时",
	ERROR_AUTH_TOKEN:               "Token生成失败",
	ERROR_AUTH:                     "Token错误",
	ERROR_AUTH_CHECK_TOKEN_EMPTY:   "Token参数不能为空",
}

func GetMsg(code int, Msg ...interface{}) string {
	msg, ok := MsgFlags[code]
	if ok {
		return fmt.Sprintf(msg, Msg...)
	}

	return MsgFlags[ERROR]
}

3、完善models

完善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) {
	if err := 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) {
	if err := 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
}

4、完善控制器

完善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, "查询成功"), "data": data})
}

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

	res, err := models.GetUser(tool.StringToInt(id))
	if err != nil {
		c.JSONP(200, gin.H{"error_code": 40001, "msg": tool.GetMsg(40001, "暂无数据"), "err": fmt.Sprint(err)})
	} else {
		c.JSONP(200, gin.H{"error_code": 0, "msg": tool.GetMsg(0, "查询成功"), "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, "名称不能为空")})
	} 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, "创建成功"), "data": data})
			} else {
				c.JSONP(200, gin.H{"error_code": 40001, "msg": tool.GetMsg(40001, "创建失败")})
			}
		} else {
			c.JSONP(200, gin.H{"error_code": 40001, "msg": tool.GetMsg(40001, "该名称已存在"), "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不存在"),
		})
	} 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, "编辑成功"),
			})
		} else {
			c.JSONP(200, gin.H{
				"error_code": 40001,
				"msg":        tool.GetMsg(40001, "编辑失败"),
				"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不存在"),
		})
	} 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, "删除成功"),
					"err":        err,
				})
			} else {
				c.JSONP(200, gin.H{
					"error_code": 40001,
					"msg":        tool.GetMsg(40001, "删除失败"),
					"err":        err,
				})
			}
		} else {
			c.JSONP(200, gin.H{
				"error_code": 40001,
				"msg":        tool.GetMsg(40001, "信息不存在"),
			})
		}

	}
}

主main.go

package main

import (
    "go-api/routes"
)

func main() {
    //启动服务器
    routes.Run()
}

启动并验证

本章节我们主要处理常规API与model层的交互以及统一返回,统一错误格式

下一章节在控制器层使用gin默认的数据校验、抽离服务层、加jwt中间件对api服务权限校验

5、系列文章