golang web开发

梁丘诚
2023-12-01

目录

文章目录

前言

一、golang web是什么?

二、搭建流程

1.模块划分

2.详细开发步骤

总结



前言

例如:习惯了java springboot 开发方式,比较疑惑golang web开发的流程和模块化的区分,

就golang web整个后台模块做下梳理。


一、golang web是什么?

   golang web是指 web后台HTTP接口开发模块。

二、搭建流程

1.模块划分

project
    -app       通常是程序启动后需要加载的数据
        app.go --加载gin 路由,指向 controller,并且做了拦截 heard token,解析放入上下文
    -biz       
       --model 
           --dto --请求的struct
           --entity --实体类
       --service   
    -common    公共常量,定义返回值 struct
       common.go
    -conf      配置信息,从配置文件里面读取到 struct
       config.go //根据环境变量读取环境resorce配置信息
    -controller  访问入口
       account_controller.go
    -resource  配置文件
       app.yml   配置数据库信息,根据 环境变量获取 取哪个配置文件
       app-dev.yml
       app-test.yml
       app-prod.yml 
    -util
     token_util.go 生成和解析token  
    -test      测试包,单元测试文件
    Dockerfile docker打包文件
    go.mod -go包管理
    main.go 启动入口

2.详细开发步骤

    相信大家看完前面的包,也有很多疑惑,该怎么开始呢?

    现在就一个包,一个包,把代码拆开来

  1.     main.go 文件
import (
	"fmt"
	"app"
	"log"
)

func main() {
    //调用 app包里面的启动
	err := app.Run()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("go web start ..!")
}

  1. 2.app包, 只有一个app.go 文件
  • 使用gin路由,指向contoller包
  • 使用util 包里面的解析 head里面的token,并把 解析后的userId,放入用户信息
  • 处理跨域请求
  • 初始化 dao 数据库连接
package app

import (
	"errors"
	"fmt"
     // 引入gin web插件
	"github.com/gin-gonic/gin"
    //引入mysql插件
	_ "github.com/go-sql-driver/mysql"
    //引入 http插件
   "net/http"


    //项目 自定义包
	"project/biz/dao"
	"project/biz/service"
	"ibc/common"
	"project/controller"
	"project/util"
	
)

func Run() error {
    //初始化数据库连接
	dao.InitDB()
	defer dao.CloseDB()
    //创建service
	accountService := service.NewAccountService()
	activitiesService := service.NewOfflineActivitiesService()

    //定义路由
	router := gin.Default()
	router.GET("/", homeEndpoint)    
	accountGroup := router.Group("/account")
	{
		accountCtl := controller.NewAccountController(accountService)
		accountGroup.POST("/", wrapHandler(accountCtl.InsertAccount))
		accountGroup.GET("/:userId", wrapHandler(accountCtl.FindAccount))
		accountGroup.GET("/account-flow/:accountId",                      wrapHandler(accountCtl.FindAccountFlow))
	}
	
	return router.Run(":8080")
}


type EndpointFunc func(*gin.Context) (any, error)

//定义返回
func homeEndpoint(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{"code": "SUCCESS"})
}


// 定义统一处理器
func wrapHandler(handler EndpointFunc) gin.HandlerFunc {
	return func(c *gin.Context) {
        // 处理跨域请求
		c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
		Cors(c)
       
         
		//从header 获取token,
		var token = c.GetHeader("token")
		//解析token,如果token不存在,或者 解析错误,则返回 没权限
		userId, err := util.TokenHandle(token)
		if err != nil {
			fmt.Errorf(err.Error())
			c.JSON(http.StatusBadRequest, gin.H{"code": "ERROR", "message": "token is invalid"})
			return
		}
         //解析后 userId 放入上下文中,后面的service服务,可以获取该用户信息
		c.Set("userId", userId)
		data, err := handler(c)
		if err != nil {
			var systemErr *common.Error
			if errors.As(err, &systemErr) {
				c.JSON(systemErr.Status, common.NewErrorResult(systemErr.Code, systemErr.Msg))
			} else {
				c.JSON(http.StatusBadRequest, gin.H{"code": "ERROR", "message": err.Error()})
			}
			return
		}
		c.JSON(http.StatusOK, gin.H{"code": "SUCCESS", "data": data})
	}
}

//跨域请求处理
func Cors(context *gin.Context) {
	method := context.Request.Method
	// 必须,接受指定域的请求,可以使用*不加以限制,但不安全
	//context.Header("Access-Control-Allow-Origin", "*")
	context.Header("Access-Control-Allow-Origin", context.GetHeader("Origin"))
	fmt.Println(context.GetHeader("Origin"))
	// 必须,设置服务器支持的所有跨域请求的方法
	context.Header("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS")
	// 服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段
	context.Header("Access-Control-Allow-Headers", "Content-Type, Content-Length, Token")
	// 可选,设置XMLHttpRequest的响应对象能拿到的额外字段
	context.Header("Access-Control-Expose-Headers", "Access-Control-Allow-Headers, Token")
	// 可选,是否允许后续请求携带认证信息Cookir,该值只能是true,不需要则不设置
	context.Header("Access-Control-Allow-Credentials", "true")
	// 放行所有OPTIONS方法
	if method == "OPTIONS" {
		context.AbortWithStatus(http.StatusNoContent)
		return
	}
	context.Next()
}

2.dao包

  • 初始化连接
  • 业务dao层
    package dao
    
    import (
    	"fmt"
        //引入 sqlx  
    	"github.com/jmoiron/sqlx"
        //项目内的 配置信息
    	"project/conf"
    	"log"
    )
    
    var BaseDb sqlx.DB
    
    // app.go 里面调用
    func InitDB() {
    
    	datasource := fmt.Sprintf("%s:%s@tcp(%s)/%s?parseTime=true&charset=utf8", conf.Config.Mysql.User, conf.Config.Mysql.Passwd, conf.Config.Mysql.Host, conf.Config.Mysql.DBName)
    	db, err := sqlx.Open("mysql", datasource)
    	if err != nil {
    		log.Fatal(err)
    		return
    	}
    	BaseDb = *db
    	
    }
    
    // app.go 里面调用
    func CloseDB() {
    	err := BaseDb.Close()
    	if err != nil {
    		log.Fatal(err)
    	}
    }
    
    package dao
    
    import (
    	"database/sql"
    	"fmt"
    	"project/biz/model/entity"
    	"strings"
    )
    
    type UserAccountRepository interface {
    	Insert(account entity.UserAccount) error
    	Update(account entity.UserAccount) error
    	DeleteById(id string) (bool, error)
    	FindById(userId string) (entity.UserAccount, error)
    	ListAll() ([]entity.UserAccount, error)
    	ExistsByUserId(userId string) (error, bool)
    }
    type UserAccountFlowRepository interface {
    	Insert(accountFlow entity.UserAccountFlow) (int, error)
    	DeleteById(id string) (bool, error)
    	FindById(id string) (entity.UserAccountFlow, error)
    	ListAll() ([]entity.UserAccountFlow, error)
    	ListByAccountId(accountId string) (*[]entity.UserAccountFlow, error)
    	FindByActivities(id string, id2 int64) (*[]entity.UserAccountFlow, error)
    }
    
    type accountRepo struct {
    }
    
    type accountFlowRepo struct {
    }
    
    //NewAccountRepository 申明实现了 UserAccountRepository 接口
    func NewAccountRepository() UserAccountRepository {
    	return &accountRepo{}
    }
    
    //Insert  < 具体实现 NewAccountRepository
    func (u accountRepo) Insert(account entity.UserAccount) error {
    	sql := `insert into user_account(id,user_id,blockchain_address,balance)values(:id,:user_id,:blockchain_address,:balance)`
    	result, err := BaseDb.NamedExec(sql, &account)
    	if err != nil {
    		return err
    	}
    	fmt.Println(result)
    
    	return nil
    
    }
    func (u accountRepo) ExistsByUserId(userId string) (error, bool) {
    	sqlStr := `select * from user_account where user_id=?`
    	var account entity.UserAccount
    	err := BaseDb.Get(&account, sqlStr, userId)
    	if err == sql.ErrNoRows {
    		return nil, false
    	}
    	if err != nil {
    		return err, false
    	}
    	if account == (entity.UserAccount{}) {
    		return nil, true
    	}
    
    	return nil, false
    
    }
    
    //Update  < 具体实现 NewAccountRepository
    func (u accountRepo) Update(account entity.UserAccount) error {
    
    	sqlColum := `update user_account set `
    	if account.Balance >= -1 {
    		sqlColum += ` balance =:balance ,`
    	}
    	if account.BlockchainAddress != "" {
    		sqlColum += ` blockchain_address =:blockchain_address ,`
    	}
    	sqlColum = strings.TrimSuffix(sqlColum, ",")
    	sqlColum += " where id =:id "
    
    	_, err := BaseDb.NamedExec(sqlColum, &account)
    	return err
    
    }
    
    //DeleteById  < 具体实现 NewAccountRepository
    func (u accountRepo) DeleteById(id string) (bool, error) {
    	sql := "update user_account set is_delete=1 where id =?"
    	_, err := BaseDb.Exec(sql, id)
    	if err != nil {
    		return false, err
    	}
    	return true, err
    }
    
    //FindById  < 具体实现 NewAccountRepository
    func (u accountRepo) FindById(userId string) (entity.UserAccount, error) {
    	sql := "select * from user_account where user_id=?"
    	var userAccount entity.UserAccount
    	err := BaseDb.Get(&userAccount, sql, userId)
    	if err != nil {
    		return userAccount, err
    	}
    	return userAccount, err
    
    }
    
    //ListAll  < 具体实现 NewAccountRepository
    func (u accountRepo) ListAll() ([]entity.UserAccount, error) {
    	sql := "select * from user_account"
    	var accountFlow []entity.UserAccount
    	err := BaseDb.Get(&accountFlow, sql)
    
    	return accountFlow, err
    }
    
    func NewAccountFlowRepository() UserAccountFlowRepository {
    	return &accountFlowRepo{}
    }
    
    //Insert  < 具体实现 NewAccountFlowRepository
    func (accountFlowRepo) Insert(account entity.UserAccountFlow) (int, error) {
    	sql := `insert into user_account_flow(user_id,account_id,type,amount,blockchain_address,before_balance,balance,activities_id,transaction_info)
         values(:user_id,:account_id,:type,:amount,:blockchain_address,:before_balance,:balance,:activities_id,:transaction_info)`
    	result, err := BaseDb.NamedExec(sql, account)
    	if err != nil {
    		return 0, err
    	}
    	id, err := result.LastInsertId()
    	if err != nil {
    		return 0, err
    	}
    	account.Id = id
    	return int(id), nil
    
    }
    
    //DeleteById  < 具体实现 NewAccountFlowRepository
    func (accountFlowRepo) DeleteById(id string) (bool, error) {
    	sql := "update user_account_flow set is_delete=1 where id =?"
    	_, err := BaseDb.Exec(sql, id)
    	if err != nil {
    		return false, err
    	}
    	return true, err
    }
    
    //FindById  < 具体实现 NewAccountFlowRepository
    func (accountFlowRepo) FindById(id string) (entity.UserAccountFlow, error) {
    	sql := "select * from user_account_flow where id=?"
    	var userAccount entity.UserAccountFlow
    	err := BaseDb.Get(&userAccount, sql, id)
    	if err != nil {
    		return userAccount, err
    	}
    	return userAccount, err
    
    }
    
    //ListAll  < 具体实现 NewAccountFlowRepository
    func (accountFlowRepo) ListAll() ([]entity.UserAccountFlow, error) {
    	sql := "select * from user_account_flow where is_delete =0"
    	var accountFlow []entity.UserAccountFlow
    	err := BaseDb.Get(&accountFlow, sql)
    
    	return accountFlow, err
    }
    
    //ListByAccountId  < 具体实现 NewAccountFlowRepository
    func (accountFlowRepo) ListByAccountId(accountId string) (*[]entity.UserAccountFlow, error) {
    	sqlStr := "select * from user_account_flow where is_delete =0 and account_id =?"
    	var accountFlow []entity.UserAccountFlow
    	err := BaseDb.Select(&accountFlow, sqlStr, accountId)
    	if err == sql.ErrNoRows {
    		return nil, nil
    	}
    	return &accountFlow, err
    }
    func (accountFlowRepo) FindByActivities(accountId string, activitiesId int64) (*[]entity.UserAccountFlow, error) {
    	sqlStr := "select * from user_account_flow where is_delete =0 and account_id =? and activities_id=?"
    	var accountFlow []entity.UserAccountFlow
    	err := BaseDb.Select(&accountFlow, sqlStr, accountId, activitiesId)
    	if err == sql.ErrNoRows {
    		return nil, nil
    	}
    	return &accountFlow, err
    }
    

    3.service

  • package service
    
    import (
    	"database/sql"
    	"errors"
    	"fmt"
    	"project/dao"
    	"project/biz/model/dto"
    	"project/biz/model/entity"
    	"project/util"
    	"math"
    	"strconv"
    	"strings"
    )
    
    type UserAccountService interface {
    	// InsertAccount 新增账户信息
    	InsertAccount(accountReq dto.CreateAccountRequest) (*entity.UserAccount, error)
    	// FindAccount 查找账户信息
    	FindAccount(userId string) (any, error)
    	// FindAccountFlow 根据账户ID查找账户
    	FindAccountFlow(accountId string) ([]entity.UserAccountFlow, error)
    	//RewardAccount 扫码奖励
    	RewardAccount(scanning dto.Scanning) (any, error)
    }
    
    func NewAccountService() UserAccountService {
    	return &accountService{
    		AccountRepo:           dao.NewAccountRepository(),
    		AccountFlowRepo:       dao.NewAccountFlowRepository(),
    		OfflineActivitiesRepo: dao.NewOfflineActivitiesRepo(),
    	}
    }
    
    type accountService struct {
    	AccountRepo           dao.UserAccountRepository
    	AccountFlowRepo       dao.UserAccountFlowRepository
    	OfflineActivitiesRepo dao.OfflineActivitiesRepository
    }
    
    // InsertAccount 新增账户信息
    func (me *accountService) InsertAccount(accountDto dto.CreateAccountRequest) (*entity.UserAccount, error) {
    	err, b := me.AccountRepo.ExistsByUserId(accountDto.UserId)
    	if err != nil {
    		return nil, err
    	}
    	if b {
    		return nil, err
    	}
    	node, _ := util.NewWorker(10)
    	account := entity.UserAccount{
    		UserId:            accountDto.UserId,
    		Id:                "AC" + strconv.Itoa(int(node.GetId())),
    		BlockchainAddress: accountDto.Address,
    	}
    	err = me.AccountRepo.Insert(account)
    	if err != nil {
    		return nil, err
    	}
    
    	return &account, nil
    }
    
    // FindAccount 查找账户信息
    func (me *accountService) FindAccount(userId string) (any, error) {
    	account, err := me.AccountRepo.FindById(userId)
    	if err == sql.ErrNoRows {
    		accDto := dto.CreateAccountRequest{}
    		accDto.UserId = userId
    		address, err := indiev_wallet.GetAddress()
    		if err != nil {
    			return nil, err
    		}
    		accDto.Address = address
    		accountEntity, err := me.InsertAccount(accDto)
    		if err != nil {
    			return nil, err
    		}
    		return accountEntity, nil
    	}
    	if account.BlockchainAddress != "" {
    		chainBalance := util.GetBalance(account.BlockchainAddress)
    		chainBalance = strings.TrimLeft(chainBalance, "0x")
    		f, err := strconv.ParseUint(chainBalance, 16, 32)
    		if err != nil {
    			return nil, err
    		}
    		//金额转换
    		account.Balance = int64(float64(f) / math.Pow(10, 8))
    
    	}
    	return account, err
    
    }
    
    // FindAccountFlow 根据账户ID查找账户
    func (me *accountService) FindAccountFlow(accountId string) ([]entity.UserAccountFlow, error) {
    	accountFlows, err := me.AccountFlowRepo.ListByAccountId(accountId)
    	if err != nil {
    		return nil, err
    	}
    	return *accountFlows, nil
    }
    
    
    type scanningDto struct {
    	RewardValue int64  `json:"rewardValue" db:"reward_value"`
    	Unit        string `json:"unit" db:"reward_value"`
    }
    

    4.controller包

  • package controller
    
    import (
    	"github.com/gin-gonic/gin"
        //项目
    	"project/biz/model/dto"
    	"project/biz/service"
    )
    
    func NewAccountController(accountService service.UserAccountService) *AccountController {
    	return &AccountController{
    		accountService: accountService,
    	}
    }
    
    // InsertAccount 新增账户信息
    func (me *AccountController) InsertAccount(c *gin.Context) (any, error) {
    	var req dto.CreateAccountRequest
    	err := c.ShouldBindJSON(&req)
    	if err != nil {
    		return nil, err
    	}
    
    	account, err := me.accountService.InsertAccount(req)
    	if err != nil {
    		return nil, err
    	}
    	return account, err
    }
    
    // FindAccount 查找账户信息
    func (me *AccountController) FindAccount(c *gin.Context) (any, error) {
    	userId := c.Param("userId")
    	account, err := me.accountService.FindAccount(userId)
    	if err != nil {
    		return nil, err
    	}
    	return account, err
    
    }
    
    // FindAccountFlow 根据账户ID查找账户
    func (me *AccountController) FindAccountFlow(c *gin.Context) (any, error) {
    	accountId := c.Param("accountId")
    	account, err := me.accountService.FindAccountFlow(accountId)
    	if err != nil {
    		return nil, err
    	}
    	return account, err
    }
    
    type AccountController struct {
    	accountService service.UserAccountService
    }
    

    5.common包

  • 返回struct

  • package common
    
    import "net/http"
    
    const (
    	CodeSuccess        = "SUCCESS"
    	CodeError          = "ERROR"
    	ParamFormatError   = "PARAM_FORMAT_ERROR"
    	ParamNotExistError = "PARAM_NOT_EXIST_ERROR"
    	AppNotExistError   = "APP_NOT_EXIST_ERROR"
    	AppExistError      = "APP_EXIST_ERROR"
    
    	InternalServiceErr = "INTERNAL_SERVICE_ERROR"
    
    	Unauthorized = "UNAUTHORIZED"
    )
    
    type Result struct {
    	Code    string `json:"code"`
    	Data    any    `json:"data"`
    	Message string `json:"message"`
    }
    
    func NewOkResult(msg string) *Result {
    	return &Result{
    		Code:    CodeSuccess,
    		Data:    nil,
    		Message: msg,
    	}
    }
    
    func NewDataResult(data any) *Result {
    	return &Result{
    		Code:    CodeSuccess,
    		Data:    data,
    		Message: "",
    	}
    }
    
    func NewErrorResult(code, msg string) *Result {
    	return &Result{
    		Code:    code,
    		Data:    nil,
    		Message: msg,
    	}
    }
    
    type Error struct {
    	Status int
    	Code   string
    	Msg    string
    }
    
    func BadRequestErr(code string, msg string) error {
    	return &Error{
    		Status: http.StatusBadRequest,
    		Code:   code,
    		Msg:    msg,
    	}
    }
    
    func NotFoundErr(code string, msg string) error {
    	return &Error{
    		Status: http.StatusNotFound,
    		Code:   code,
    		Msg:    msg,
    	}
    }
    
    func UnauthorizedErr() error {
    	return &Error{
    		Status: http.StatusUnauthorized,
    		Code:   "Unauthorized",
    		Msg:    "",
    	}
    }
    
    func InternalServerErr(code string, msg string) error {
    	return &Error{
    		Status: http.StatusInternalServerError,
    		Code:   code,
    		Msg:    msg,
    	}
    }
    
    func (e *Error) Error() string {
    	return e.Msg
    }
    

    6.config包

  •  

    package conf
    
    import (
    	log "github.com/sirupsen/logrus"
    	"github.com/spf13/viper"
    	"os"
    )
    //数据库配置
    type DbConfig struct {
    	Host   string
    	User   string
    	Passwd string
    	DBName string
    }
    
    type Struct struct {
    	Mysql DbConfig
    }
    
    var Config Struct
    
    //初始化,优先加载
    func init() {
    	var appName = "app"
        //路径
    	viper.AddConfigPath("./resource")
         //如果 环境信息,或者命令行有 这个变量,则追加环境信息,如果没有,默认取 app.yml 
         //加载了环境则是 app-dev.yml |app-test.yml
    	configEnv := os.Getenv("GO_ENV")
    
    	if configEnv != "" {
    		appName += "-" + configEnv
    	}
    
    	viper.SetConfigName(appName)
    	if err := viper.ReadInConfig(); err != nil {
    		log.Panicf("Error reading config file  %s\n", err)
    	}
        // viper读取了配置,并且将 json字符 反序列化 到对象
    	err := viper.Unmarshal(&Config)
    	if err != nil {
    		log.Panicf("unable to decode into struct, %v", err)
    	}
    
    }
    

    7.util包

  • package util
    
    import (
    	"encoding/json"
    	"fmt"
    	"github.com/golang-jwt/jwt"
    	_ "github.com/golang-jwt/jwt"
    	"log"
    	"time"
    )
    // 秘钥,如果要对接,问问上游生成的时候,有没有 秘钥
    var mySigningKey = []byte("asfasfdafasdfdasfa.")
    //TokenHandle 解析token
    
    func TokenHandle(tokenString string) (string, error) {
    	token, err := jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {
    		return mySigningKey, nil
    	})
    	if err != nil {
    		log.Println(err.Error())
    		return "", err
    	}
    	data := token.Claims.(jwt.MapClaims)["data"]
    	var tokenData = new(TokenData)
    	if data != nil {
    		var info = data.(string)
    		err = json.Unmarshal([]byte(info), &tokenData)
    	}
    
    	return tokenData.UserId, err
    }
    //CreateToken is created token 
    func CreateToken(data TokenData) (string, error) {
    	dataByte, err := json.Marshal(data)
    	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
    		"data": string(dataByte),
    		"exp":  time.Now().Unix() + 1000*5,
    		"iss":  "ibc_business",
    	})
    
    	tokenString, err := token.SignedString(mySigningKey)
    	if err != nil {
    		log.Fatal(err)
    		return "", err
    	}
    	fmt.Println("加密后的token字符串", tokenString)
    	return tokenString, nil
    }
    
    //TokenData is 用户对象
    type TokenData struct {
    	UserId   string
    	Age      int32
    	NickName string
    	Name     string
    	Phone    string
    }
    

    8.go.mod

    module project //项目名
    
    go 1.18
    
    require (
    	github.com/33cn/chain33 v1.67.3
    	github.com/gin-gonic/gin v1.8.1
    	github.com/go-sql-driver/mysql v1.6.0
    	github.com/golang-jwt/jwt v3.2.1+incompatible
    	github.com/jmoiron/sqlx v1.3.5
    	github.com/miguelmota/go-ethereum-hdwallet v0.1.1
    	github.com/polydawn/refmt v0.0.0-20190807091052-3d65705ee9f1
    	github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b
    	github.com/sirupsen/logrus v1.9.0
    	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
    	github.com/spf13/viper v1.13.0
    )
    
    require (
    	github.com/BurntSushi/toml v0.3.1 // indirect
    	github.com/btcsuite/btcd v0.22.0-beta // indirect
    	github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce // indirect
    	github.com/decred/base58 v1.0.3 // indirect
    	github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect
    	github.com/ethereum/go-ethereum v1.10.16 // indirect
    	github.com/fsnotify/fsnotify v1.5.4 // indirect
    	github.com/gin-contrib/sse v0.1.0 // indirect
    	github.com/go-playground/locales v0.14.0 // indirect
    	github.com/go-playground/universal-translator v0.18.0 // indirect
    	github.com/go-playground/validator/v10 v10.10.0 // indirect
    	github.com/go-stack/stack v1.8.0 // indirect
    	github.com/goccy/go-json v0.9.7 // indirect
    	github.com/golang/protobuf v1.5.2 // indirect
    	github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
    	github.com/hashicorp/hcl v1.0.0 // indirect
    	github.com/json-iterator/go v1.1.12 // indirect
    	github.com/leodido/go-urn v1.2.1 // indirect
    	github.com/magiconair/properties v1.8.6 // indirect
    	github.com/mattn/go-colorable v0.1.12 // indirect
    	github.com/mattn/go-isatty v0.0.14 // indirect
    	github.com/mitchellh/mapstructure v1.5.0 // indirect
    	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
    	github.com/modern-go/reflect2 v1.0.2 // indirect
    	github.com/mr-tron/base58 v1.2.0 // indirect
    	github.com/pelletier/go-toml v1.9.5 // indirect
    	github.com/pelletier/go-toml/v2 v2.0.5 // indirect
    	github.com/pkg/errors v0.9.1 // indirect
    	github.com/prometheus/client_golang v1.11.1 // indirect
    	github.com/shopspring/decimal v1.2.0 // indirect
    	github.com/spf13/afero v1.8.2 // indirect
    	github.com/spf13/cast v1.5.0 // indirect
    	github.com/spf13/jwalterweatherman v1.1.0 // indirect
    	github.com/spf13/pflag v1.0.5 // indirect
    	github.com/subosito/gotenv v1.4.1 // indirect
    	github.com/tjfoc/gmsm v1.3.2 // indirect
    	github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef // indirect
    	github.com/ugorji/go/codec v1.2.7 // indirect
    	golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
    	golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 // indirect
    	golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect
    	golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
    	golang.org/x/text v0.3.7 // indirect
    	google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect
    	google.golang.org/grpc v1.46.2 // indirect
    	google.golang.org/protobuf v1.28.0 // indirect
    	gopkg.in/ini.v1 v1.67.0 // indirect
    	gopkg.in/yaml.v2 v2.4.0 // indirect
    	gopkg.in/yaml.v3 v3.0.1 // indirect
    )
    


总结

以上就是 golang搭建web项目的流程,使用gin 做http请求,同时封装了标准返回,拦截token.并且使用sqlx做 mysql的连接。

 类似资料: