在實際開發中,需要前後端需要協商狀態碼,狀態碼用於後端返前端時使用。在一個團隊中,定義的狀態碼講道理應該是一致的,專案開始的起始階段狀態碼應該是定義了個七七八八的,隨著功能的疊加而不斷增加。此係列將圍繞我的研發之旅進行。
狀態碼推薦的專案目錄為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] routes
│ └── [ 343] routes.go
├── [ 96] routes
│ └── [ 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 不記錄日誌
routes層
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(¶m); err != nil {
Response(c, globalcode.PARAMETER_ERR, err.Error())
return
}
param.Name = strings.TrimSpace(param.Name)
if param.Name == "zise" {
name := service.CodeDemo(param.Name)
Response(c, globalcode.SUCCESS ,name)
} else {
// 模仿自定義狀態碼然後返回前端
}
}
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 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
)
message.go
- 狀態碼對應的資訊
package globalcode
// Msg 全域性狀態碼
var Msg = map[int]string{
SUCCESS: "請求成功",
PARAMETER_ERR: "請求引數錯誤",
驗證
- 錯誤code
- 正確code