紫色飛豬的研發之旅--06go自定義狀態碼


在實際開發中,需要前后端需要協商狀態碼,狀態碼用於后端返前端時使用。在一個團隊中,定義的狀態碼講道理應該是一致的,項目開始的起始階段狀態碼應該是定義了個七七八八的,隨着功能的疊加而不斷增加。此系列將圍繞我的研發之旅進行。

狀態碼推薦的項目目錄為pkg/globalcode目錄

后端返前端的json 格式為:

{
    "code": 狀態碼,
    "data": [
        功能邏輯后返前字段
    ],
    "message": 狀態碼對應的message
}

本片將以成功參數校驗兩個常見的狀態碼為案例進行

自定義狀態碼 目錄結構

.
├── [  96]  cmd
│   └── [1.3K]  root.go
├── [ 128]  config
│   ├── [2.2K]  cfg.go
│   └── [ 129]  config.yaml
├── [ 160]  controller
│   ├── [ 267]  base.go
│   ├── [ 452]  name.go
│   └── [  96]  validation   # 校驗層
│       └── [ 151]  name.go
├── [ 242]  go.mod
├── [ 66K]  go.sum
├── [ 200]  main.go
├── [  96]  pkg
│   └── [ 128]  globalcode
│       ├── [  79]  code.go
│       └── [ 148]  message.go
├── [  96]  router
│   └── [ 343]  routes.go
└── [  96]  service
          └──[  80]  name.go


8 directories, 13 files

邏輯:項目初始化 --> 前端調接口--> 后端routes層 --> 后端controller層(判斷前端傳參是否有無(如果沒有前端傳參跳過此步驟) )--> 后端service層處理功能邏輯(有可能需要model層) --> 后端controller層處理返回結果(這里需要狀態碼處理)

項目初始化

main.go

/**
 * @Author: zisefeizhu
 * @Description: code
 * @File:  main.go
 * @Version: 1.0.0
 * @Date: 2021/9/4 10:13
 */

package main

import (
	"codedemo/cmd"
)

func main() {
	//入口
	cmd.Execute()
}

cmd/root.go

package cmd

import (
	"codedemo/config"
	"codedemo/routes"
	"fmt"
	"os"

	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var (
	cfgFile string
	serverPort int
)


var rootCmd = &cobra.Command{
	Use:   "server",
	Short: "about the code",
	Long:  "summary of status codes from zisefeizhu",
	Run: func(cmd *cobra.Command, args []string) {
		fmt.Println("啟動參數: ", args)
		httpServer()
	},
}

func init() {
	logrus.Infoln("init root.go...")
	cobra.OnInitialize(initConifg)
	rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $CURRENT_DIR/config/config.yaml)")
	rootCmd.Flags().IntVarP(&serverPort, "port", "p", 9001, "port on which the server will listen")
}

// 初始化配置
func initConifg() {
	config.Loader(cfgFile)
	config.InitLog()
}

func httpServer() {
	logrus.Infoln("server start...")
	defer func() {
		logrus.Infoln("server exit..")
	}()
	//設置模式,設置模式要放在調用Default()函數之前
	gin.SetMode(viper.GetString("runmode"))
	logrus.Infoln("runmode: ", viper.GetString("runmode"))

	// 路由設置
	g := gin.Default()
	routes.Init(g)
	g.Run(fmt.Sprintf(":%d", serverPort))

}

// Execute rootCmd
func Execute() {
	if err := rootCmd.Execute(); err != nil {
		logrus.Fatalln(err)
		os.Exit(1)
	}
}

config/cfg.go

package config

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
	"github.com/spf13/viper"
	"io"
	"os"
	"path/filepath"
	"strings"
)

// Loader 加載配置文件
func Loader(cfgFile string) {
	if cfgFile == "" {
		path, _ := os.Getwd()
		cfgFile = path + "/config/config.yaml"
		fmt.Println(cfgFile)
	}

	viper.SetConfigFile(cfgFile)              //用來指定配置文件的名稱
	viper.SetEnvPrefix("ENV")                 //SetEnvPrefix會設置一個環境變量的前綴名
	viper.AutomaticEnv()                      //會獲取所有的環境變量,同時如果設置過了前綴則會自動補全前綴名
	replacer := strings.NewReplacer(".", "_") //NewReplacer() 使用提供的多組old、new字符串對創建並返回一個*Replacer
	viper.SetEnvKeyReplacer(replacer)

	if err := viper.ReadInConfig(); err != nil {
		fmt.Printf("config file error: %s\n", err)
		os.Exit(1)
	}
}

// InitLog 初始化日志
func InitLog() {
	// log.logrus_json
	if viper.GetBool("log.logrus_json") {
		logrus.SetFormatter(&logrus.JSONFormatter{})
	}

	// log.logrus_level
	switch viper.GetString("log.logrus_level") {
	case "trace":
		logrus.SetLevel(logrus.TraceLevel)
	case "debug":
		logrus.SetLevel(logrus.DebugLevel)
	case "info":
		logrus.SetLevel(logrus.InfoLevel)
	case "warn":
		logrus.SetLevel(logrus.WarnLevel)
	case "error":
		logrus.SetLevel(logrus.ErrorLevel)
	}

	// log.logrus_file
	if viper.GetBool("log.file") {
		logrusFile := viper.GetString("log.logrus_file")
		os.MkdirAll(filepath.Dir(logrusFile), os.ModePerm)

		file, err := os.OpenFile(logrusFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
		if err == nil {
			if viper.GetBool("log.logrus_console") {
				logrus.SetOutput(io.MultiWriter(file, os.Stdout))
			} else {
				logrus.SetOutput(file)
			}
		}

		// log.gin_file & log.gin_console
		ginFile := viper.GetString("log.gin_file")
		os.MkdirAll(filepath.Dir(ginFile), os.ModePerm)

		file, err = os.OpenFile(ginFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
		if err == nil {
			if viper.GetBool("log.gin_console") {
				gin.DefaultWriter = io.MultiWriter(file, os.Stdout)
			} else {
				gin.DefaultWriter = io.MultiWriter(file)
			}
		}
	}

	// default
	logrus.SetReportCaller(true)
}

config/config.yaml

prefix_path: /zisefeizhu/api/v1 #api路徑
gormlog: true # gorm 的日志模式, true 是詳細日志, false 不記錄日志

router層

routes.go

package routes

import (
	"codedemo/controller"
	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
	"github.com/spf13/viper"
)

func Init(g *gin.Engine) {
	prefixPath := viper.GetString("prefix_path")
	g.Use(cors.Default())
	// 集群
	codedemo := g.Group(prefixPath + "/code")
	{
		codedemo.GET("/codedemo",controller.GetName)
	}
}

controller 層

name.go

package controller

import (
	"codedemo/controller/validation"
	"codedemo/pkg/globalcode"
	"codedemo/service"
	"github.com/gin-gonic/gin"
	"strings"
)

func GetName(c *gin.Context)()  {
	var param  validation.GetNameRepos
	if err := c.ShouldBind(&param); err != nil {
		ErrResponse(c, globalcode.PARAMETER_ERR, err.Error())
		return
	}
	param.Name = strings.TrimSpace(param.Name)
	if param.Name == "zise" {
		name := service.CodeDemo(param.Name)
		SuccessResponse(c ,name)
	} else {
		data := "name 必須是zise"
		ErrResponse(c, globalcode.PARAMETER_MUST_ZISE, data)
	}
}

validation/name.go

package validation

// GetNameRepos 前端param
type GetNameRepos struct {
	Name string `json:"name" form:"name" binding:"required"`  		// 姓名不能為空
}

base.go

package controller

import (
	"codedemo/pkg/globalcode"
	"github.com/gin-gonic/gin"
	"net/http"
)

func ErrResponse(c *gin.Context, code int, data interface{}) {
	response(c, code, data)
}

func SuccessResponse(c *gin.Context, data interface{}) {
	response(c, globalcode.SUCCESS, data)
}

// SuccessPaginateResponse 成功返回帶分頁
func SuccessPaginateResponse(c *gin.Context, data interface{}, total int, curPage int, curPageSize int) {
	pageResponse := gin.H{
		"cur_page":      curPage,
		"cur_page_size": curPageSize,
		"total":         total,
		"total_page":    total / curPageSize,
		"data":          data,
	}
	response(c, globalcode.SUCCESS, pageResponse)
}

func response(c *gin.Context, code int, data interface{}) {
	c.JSON(http.StatusOK, gin.H{
		"code":    code,
		"message": globalcode.Msg[code],
		"data":    data,
	})
}

service 層

name.go

package service

func CodeDemo(name string)  string {
	return name + "feizhu"
}

pkg/globalcode 層

  • 狀態碼處理,本篇的重心

code.go

  • 定義狀態碼
package globalcode

var (
	SUCCESS     	  		= 200
	PARAMETER_ERR     		= 10000
	PARAMETER_MUST_ZISE 	= 10001
)

message.go

  • 狀態碼對應的信息
package globalcode


// Msg 全局狀態碼
var Msg = map[int]string{
	SUCCESS:       			"請求成功",
	PARAMETER_ERR:      	"請求參數錯誤",
	PARAMETER_MUST_ZISE: 	"參數必須是zise",
}

驗證

    1. 請求參數錯誤
      參數校驗錯誤
    1. 參數傳入正確但非zise
      參數傳入正確但非zise
    1. 正確code
      正確code


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM