golang web开发

2023-11-08

目录

文章目录

前言

一、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的连接。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

golang web开发 的相关文章

  • golang: Logrus实现日志打印

    Github https github com sirupsen logrus golang标准库的日志框架非常简单 仅仅提供了print panic和fatal三个函数 对于更精细的日志级别 日志文件分割以及日志分发等方面并没有提供支持
  • Golang适合高并发场景的原因分析

    典型的两个现实案例 我们先看两个用Go做消息推送的案例实际处理能力 360消息推送的数据 16台机器 标配 24个硬件线程 64GB内存 Linux Kernel 2 6 32 x86 64 单机80万并发连接 load 0 2 0 4 C
  • Go_关键字、编译、转义字符

    关键字 关键字是指被go语言赋予了特殊含义的单词 共25个 关键字不能用于自定义名字 只能在特定语法结构中使用 break default func interface select case defer go map struct cha
  • 权重实现随机抽奖

    一般抽奖是怎么实现的 在实习期间学会了一种通用的写法 在这里记录一下 最近在学Golang语法基础 这里就用Golang来写 package main import fmt time math rand func main r rand N
  • golang:环境变量GOPROXY和GO111MODULE设置

    我们安装完golang后 我们在windows的cmd命令下就可以直接查看和使用go命令和环境变量了 同样的在linux下可以在控制台使用 如下图所示 C Users lijie1 gt go env set GO111MODULE set
  • 【golang】error parsing regexp: invalid or unsupported Perl syntax (正则表达式校验密码)

    要在 Go 中编写密码校验规则 确保密码不少于8位且包含数字和字母 你可以使用正则表达式和 Go 的 regexp 包来实现 以下是一个示例代码 错误示范 package main import fmt regexp func valida
  • Golang协程与通道整理

    协程goroutine 不由OS调度 而是用户层自行释放CPU 从而在执行体之间切换 Go在底层进行协助实现 涉及系统调用的地方由Go标准库协助释放CPU 总之 不通过OS进行切换 自行切换 系统运行开支大大降低 通道channel 并发编
  • Go中 Redis Client的使用

    文章目录 常见操作 List 操作 Pipeline 使用 在 Go 语言中使用 Redis 时 可以使用第三方库实现 Redis Client 的封装 本文介绍如何使用 Go 语言的 redisClient 去连接 Redis 服务器 并
  • goland环境配置

    goland modules环境配置 下载和安装goland 环境配置 配置环境变量GOPATH 配置go modules GOPROXY代理的系统变量 工程目录中新建三个工作目录 goland中启用go modules 新建一个go程序
  • Go 语言注释教程

    注释是在执行时被忽略的文本 注释可用于解释代码 使其更易读 注释还可用于在测试替代代码时防止代码执行 Go支持单行或多行注释 Go单行注释 单行注释以两个正斜杠 开头 在 和行尾之间的任何文本都将被编译器忽略 不会被执行 示例 This i
  • 为什么最近听说 Go 岗位很少很难?

    大家好 我是煎鱼 其实这个话题已经躺在我的 TODO 里很久了 近来很多社区的小伙伴都私下来交流 也有在朋友圈看到朋友吐槽 Go 上海的大会没什么人 还不如 Rust 大会 比较尴尬 今天主要是看看为什么 Go 岗位看起来近来很难的样子 也
  • 深入理解 Go 语言中的接口(interface)

    一 GoLang 接口的定义 1 GoLang 中的接口 在 Go 语言中接口 interface 是一种类型 一种抽象的类型 接口 interface 定义了一个对象的行为规范 只定义规范不实现 由具体的对象来实现规范的细节 实现接口的条
  • 有哪些不错的 Golang 开源项目?

    目前人在字节做 Go 开发 寻找 Golang 开源项目学习目的可能是 想学习或者提高自己对 Go 项目的组织和编排能力 想学习 Go 项目的框架设计 想在一些 Go 语法上细节的优化和进阶 我推荐两个项目 一 tinode 这是一个开源的
  • go-zero开发入门之gateway深入研究1

    创建一个 gateway 示例 main go package main import flag fmt gateway middleware github com zeromicro go zero core conf github co
  • go-zero 的 etcd 配置

    实现代码在 core discov config go 文件中 type EtcdConf struct Hosts string Key string ID int64 json optional User string json opt
  • go-zero开发入门-API网关鉴权开发示例

    本文是 go zero开发入门 API网关开发示例 一文的延伸 继续之前请先阅读此文 在项目根目录下创建子目录 middleware 在此目录下创建文件 auth go 内容如下 鉴权中间件 package middleware impor
  • 【go语言】error错误机制及自定义错误返回类型

    简介 Go 语言通过内置的 error 接口来处理错误 该接口定义如下 type error interface Error string 这意味着任何实现了 Error 方法的类型都可以作为错误类型 在 Go 中 通常使用 errors
  • go开发--操作mysql数据库

    在 Go 中访问 MySQL 数据库并进行读写操作通常需要使用第三方的 MySQL 驱动 Go 中常用的 MySQL 驱动有 github com go sql driver mysql 和 github com go xorm xorm
  • 【go语言】结构体数据填充生成md错误码文件

    这里使用pongo2这个模版引擎库进行md文件渲染 GitHub flosch pongo2 Django syntax like template engine for Go package main import fmt github
  • 【go语言】读取toml文件

    一 简介 TOML 全称为Tom s Obvious Minimal Language 是一种易读的配置文件格式 旨在成为一个极简的数据序列化语言 TOML的设计原则之一是保持简洁性 易读性 同时提供足够的灵活性以满足各种应用场景 TOML

随机推荐

  • 原生ajax运行原理,【前端自学之路】JS之原生AJAX原理

    Javascript Ajax 原理 什么是 AJAX AJAX Asynchronous JavaScript and XML 异步Javascript 和 XML AJAX 是指一种创建动态网页的开发技术 说白话点 AJAX 就是允许J
  • Android常见问题debug

    android util Log 常用的方法有以下5个 Log v Log d Log i Log w Log e 根据首字母对应 VERBOSE DEBUG INFO WARN ERROR 另外 Log太多时用来过滤和标识分类log信息
  • Git简单入门(学习笔记)

    Git简单入门 学习笔记 目录 一 Git概念 二 版本控制 三 Git下载与安装 四 Git结构 五 本地库与远程库的交互方式 六 代码托管中心 七 初始化本地仓库 一 Git概念 Git是一个免费的开源的分布式版本控制系统 可以快速高效
  • python在线评测系统_关于开源OJ_在线评测系统(Online Judge)设计与实现的研究与分析...

    标签 OJ是Online Judge系统的简称 用来在线检测程序源代码的正确性 著名的OJ有TYVJ RQNOJ URAL等 国内著名的题库有北京大学题库 浙江大学题库 电子科技大学题库 杭州电子科技大学等 国外的题库包括乌拉尔大学 瓦拉杜
  • LVS 之 集群搭建

    官网地址 http www linuxvirtualserver org zh lvs1 html 首先 准备4台虚拟机 一个用于客户端 一个用于LVS 调度器 2个用于后端服务器 LVS NAT配置 1 zk02 开启内核的核心转发功能
  • 在Python中使用StanfordOpenIE

    本文在 维基百科数据预处理的基础上进行 1 StanfordOpenIE简介 开放信息提取 open IE 是指从纯文本中提取关系元组 通常是二元关系 例如 Mark Zuckerberg 脸书 与其他信息提取的核心区别在于 这些关系的模式
  • mysql配置和使用中可能会出现的若干问题

    Manually delete the data folder created by yourself 删除自行创建的data文件夹 Then enter the bin directory under the administrator
  • QT应用开发基础

    目录 前言 Windows上搭建开发环境 C 基础 什么是C 什么是面向对象 什么又是面向过程 c 的灵魂 c 的类 对象 类的实例化 怎么访问类的成员 类的函数成员 类的访问修饰符 函数的重载 构造函数和析构函数 类的继承 虚函数和纯虚函
  • Android Studio 的NotePad制作(日志本)

    自己写的NotePad 一个星期左右的时间 完成了最基本的功能 但是 界面还是一如既往的shi 因为百度找的图标都不是那种成套的 想找的找不到 干脆下次自己画 NotePad的功能无非是对日志的增删改查 这次还加入了Preference的一
  • java零基础从入门到精通(全)

    目录 前言 1 入门知识 1 1 JDK JRE JVM区别 1 2 编译与运行理解 1 3 类体函数细节 2 语法 2 1 标识符与关键字 2 2 变量与数据类型 2 3 控制语句 3 方法 3 1 定义 3 2 方法重载 3 3 方法递
  • electron开发环境搭建

    开发环境 Node js Vscode vscode安装Debugger for Chrome 创建开发目录 也是解决方案 执行初始化命令 创建electronpicture工程 并添加main js和index html文件 npm in
  • 存储器容量、位宽及其地址线根数三者之间的关系

    转载于 http blog sina com cn s blog 498dc96f0100gc2r html 1 存储器 Flash ROM SST39VF1601 数据位宽为16位 16根数据线 20根地址线 2M 1M 16bit SD
  • PID算法(一)PID简介

    PID算法简介及实现代码 PID简介 智能车比赛中 用到了PID算法 写下来当一个总结 PID是很经典且应用很广泛的控制算法 依据误差来减少误差 PID PID分为三部分 P 比例 P增大 可以加快系统响应速度 但是不能从根本上消除静态误差
  • 搭建ChatGPT对话式小说

    牙叔教程 简单易懂 你只需要写一个开头 剩下的交给ChatGPT 视频查看效果 两个ChatGPT互聊 写小说 哔哩哔哩 bilibili 这是一种ChatGPT的展现方式 他把你主动问ChatGPT的这种方式 改为了ChatGPT和Cha
  • 双指针算法(acwing)疑难讲解

    相信大家都是看完y总的课来看博客解惑的我会在这里分享一些我理解的细节 回顾一下题目 直接上代码 include
  • VUE element-ui之百度OCR证件识别,组件内证件照转码解码识别身份证照文字

    步骤 接上一篇博文继续完善获取access token 封装Base64照片转码方法 const identification 将图片转为base64格式 transformBase64 file return new Promise re
  • 毕业设计-基于 MATLAB 的小波去噪的研究

    目录 前言 课题背景和意义 实现技术思路 一 小波理论 二 小波去噪原理 三 去噪实验仿真 实现效果图样例 最后 前言 大四是整个大学期间最忙碌的时光 一边要忙着备考或实习为毕业后面临的就业升学做准备 一边要为毕业设计耗费大量精力 近几年各
  • 表的内连接、外连接(左连接与右连接)

    内连接 利用内连接可获取两表的公共部分的记录 语句如下 Select from A JOIN B ON A Aid B Bid 外连接 外连接分为两种 一种是左连接 Left JOIN 和右连接 Right JOIN 1 左连接 Left
  • latex公式对齐方式详解

    符号和 控制公式左对齐 对齐结果如下 begin array 控制公式对齐 begin array 是什么 begin array 是LaTeX中用于创建数学数组 math array 的环境 它可以用于在数学模式下创建一个由多行多列组成的
  • golang web开发

    目录 文章目录 前言 一 golang web是什么 二 搭建流程 1 模块划分 2 详细开发步骤 总结 前言 例如 习惯了java springboot 开发方式 比较疑惑golang web开发的流程和模块化的区分 就golang we