目录结构:

`-- demo
|-- cmd
| |-- api.go
| `-- root.go
|-- common
| `-- consts
| `-- consts.go
|-- config
| `-- viper.go
|-- config.toml
|-- go.mod
|-- go.sum
|-- handler
| |-- jwt.go
| |-- h_hello.go
| |-- helper.go
|-- main.go
|-- model
| |-- db.go
| |-- m_user.go
| |-- helper.go
|-- scripts
| |-- Readme.md
| |-- package_cms.sh
| `-- package_tar.sh
|-- service
| |-- product_service.go
`-- util

main.go

package main

import "demo/cmd"

var buildTime, gitHash string

func main() {
cmd.Execute(buildTime, gitHash)
}

cmd/root.go

package cmd

import (
"github.com/fatih/color"
"github.com/spf13/cobra"
"os"
"demo/config"
"demo/util/log"
"runtime"
) var rootCmd = &cobra.Command{
Use: "demo",
Short: "demo",
Long: "demo",
Run: func(cmd *cobra.Command, args []string) {
if isShowVersion {
color.HiYellow("Golang Env: %s %s/%s", runtime.Version(), runtime.GOOS, runtime.GOARCH)
color.Cyan("UTC build time: %s", buildTime)
color.Yellow("Build from repo version: %s", gitHash) }
},
} var buildTime, gitHash, configFile string
var verbose, isShowVersion bool func Execute(bTime, gHash string) {
buildTime = bTime
gitHash = gHash
if err := rootCmd.Execute(); err != nil {
log.Error(err)
os.Exit(1)
}
} func init() {
cobra.OnInitialize(initFunc)
rootCmd.Flags().BoolVarP(&isShowVersion, "version", "V", false, "show binary build infomation")
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", true, "verbose")
rootCmd.PersistentFlags().StringVar(&configFile, "config", "config", "config file path")
} func initFunc() {
config.InitViper(configFile)
}
package cmd

import (
"fmt"
"pms/config"
"pms/handler"
"pms/model"
"pms/util/log"
"time" "github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
"github.com/go-co-op/gocron"
"github.com/spf13/cobra"
) var apiCmd = &cobra.Command{
Use: "api",
Short: "run asset crawler api",
Long: "",
Run: func(cmd *cobra.Command, args []string) {
port := config.GetInt("app.port")
addr := fmt.Sprintf("0.0.0.0:%d", port)
log.Debug("start demo api...")
if err := initDb(); err != nil {
log.Fatal("数据库初始化失败:", err)
} if err := initCorn(); err != nil {
log.Fatal("定时任务注册失败:", err)
} if err := runRESTFulAPI(addr); err != nil {
log.Fatal("run pms api failed, err : ", err)
}
},
} func init() {
rootCmd.AddCommand(apiCmd)
} func runRESTFulAPI(addr string) error {
r := gin.New()
registerGinHandler(r)
return r.Run(addr)
} func initDb() error {
var db model.DB
db = new(model.LocalDb)
err := db.Create()
if err != nil {
fmt.Println("LocalDb 初始化连接失败")
return err
}
//defer db.Close() db = new(model.ProductDb)
if err := db.Create(); err != nil {
fmt.Println("ProductDb 初始化连接失败")
return err
}
//defer db.Close()
return nil
} func initCorn() error {
timezone, _ := time.LoadLocation("Asia/Shanghai")
s := gocron.NewScheduler(timezone)
//每周五晚上10点准时打包
timeStr := config.GetString("cron.tar_time_set")
_, err := s.Every(1).Day().At(timeStr).Do(func() {
go handler.ReleaseFromCorn()
})
if err != nil {
fmt.Println("定时任务初始化失败", err)
}
s.StartAsync()
return nil
} // 注册路由
func registerGinHandler(r *gin.Engine) {
apiG := r.Group("/api/v1")
apiG.Use(crossDomainMiddleware())
store := cookie.NewStore([]byte("demo"))
apiG.Use(sessions.Sessions("sessions", store))
apiG.GET("hello", handler.Hello)
apiG.POST("login", handler.Login)
apiG.Static("images", handler.GetImagePath()) //登陆认证组
loginG := apiG.Use(handler.JWTCheck())
{
loginG.GET("auth_hello", handler.Hello)
}
} //跨域中间件
func crossDomainMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
c.Header("Access-Control-Allow-Origin", "*")
c.Next()
latency := time.Since(start)
log.Infof("%s %s cost: %v.", c.Request.Method, c.Request.RequestURI, latency)
}
}

config/config.go

package config

import (
"github.com/spf13/viper"
"demo/util/log"
) func InitViper(configFile string) {
viper.SetConfigName(configFile)
viper.AddConfigPath(".")
err := viper.ReadInConfig()
if err != nil {
log.Error("application configuration'initialization is failed ", err)
}
} func GetString(key string) string {
return viper.GetString(key)
} func GetInt(key string) int {
return viper.GetInt(key)
} func GetBool(key string) bool {
return viper.GetBool(key)
} func GetStringMapString(key string) map[string]string {
return viper.GetStringMapString(key)
} func GetStringSlice(key string) []string {
return viper.GetStringSlice(key)
}

db.go

package model

import (
"fmt"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
"github.com/sirupsen/logrus"
"demo/config"
) var db *gorm.DB
var productDb *gorm.DB type DB interface {
Create() error
Close()
} type LocalDb struct {
} func (l LocalDb) Create() error {
dbUser := config.GetString("db.user")
dbHost := config.GetString("db.host")
dbPassword := config.GetString("db.password")
dbPort := config.GetInt("db.port")
dbName := config.GetString("db.dbname")
dbSqlMode := config.GetString("db.sql_mode") conn := fmt.Sprintf("%s:%s@(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local&sql_mode='%s'", dbUser, dbPassword, dbHost, dbPort, dbName, dbSqlMode)
mysqlDb, err := gorm.Open("mysql", conn)
if err != nil {
return err
}
db = mysqlDb
db.LogMode(true)
logrus.New()
return nil
} func (l LocalDb) Close() {
if db != nil {
err := db.Close()
if err != nil {
logrus.WithError(err).Error("close LocalDb db failed")
}
}
}

model/jwt.go

package handler

import (
"errors"
"github.com/gin-gonic/gin"
"demo/model"
"strings"
) const tokenPrefix = "Bearer "
const bearerLen = len(tokenPrefix) func JWTCheck() gin.HandlerFunc {
return func(c *gin.Context) {
rawToken := c.GetHeader("Authorization")
if len(rawToken) < bearerLen {
handleErrorAuth(c, errors.New("未找到承载令牌"))
return
} token := strings.TrimSpace(rawToken[bearerLen:])
uid, err := model.JwtParseUid(token)
if handleErrorAuth(c, err) {
return
} c.Set(jwtAuthedUserIdKey, uid)
c.Next()
}
}

model/helper.go

package model

import (
"errors"
"fmt"
"github.com/golang-jwt/jwt/v5"
"github.com/sirupsen/logrus"
"demo/config"
"strconv"
"time"
) type jwtObj struct {
User
Token string `json:"token"`
Expire time.Time `json:"expire"`
ExpireTs int64 `json:"expire_ts"`
} func jwtGenerateToken(m *User) (*jwtObj, error) {
m.Password = ""
expireAfterTime := time.Hour * time.Duration(config.GetInt("app.jwt_expire_hour"))
iss := config.GetString("app.name")
appSecret := config.GetString("app.secret")
expireTime := time.Now().Add(expireAfterTime) claim := jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expireTime),
NotBefore: jwt.NewNumericDate(time.Now()),
IssuedAt: jwt.NewNumericDate(time.Now()),
Issuer: iss,
ID: fmt.Sprintf("%d", m.ID),
} token := jwt.NewWithClaims(jwt.SigningMethodHS256, claim)
tokenString, err := token.SignedString([]byte(appSecret))
if err != nil {
logrus.WithError(err).Fatal("config is wrong, can not generate jwt")
}
data := &jwtObj{User: *m, Token: tokenString, Expire: expireTime, ExpireTs: expireTime.Unix()}
return data, err
} func JwtParseUid(tokenString string) (uint, error) {
if tokenString == "" {
return 0, errors.New("授权承载中未找到令牌")
}
token, err := jwt.ParseWithClaims(tokenString, &jwt.RegisteredClaims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
secret := config.GetString("app.secret")
return []byte(secret), nil
})
if err != nil {
return 0, errors.New("token 解析失败" + err.Error())
} if !token.Valid {
if errors.Is(err, jwt.ErrTokenMalformed) {
return 0, errors.New("token格式错误,无法解析")
} else if errors.Is(err, jwt.ErrTokenExpired) || errors.Is(err, jwt.ErrTokenNotValidYet) {
return 0, errors.New("token时效性错误")
} else {
return 0, errors.New("token非法")
}
} if claims, ok := token.Claims.(*jwt.RegisteredClaims); ok {
uid, err := strconv.ParseUint(claims.ID, 10, 64)
return uint(uid), err
} else {
return 0, errors.New("token claims解析失败")
}
} type Pagination struct {
PageSize uint `json:"page_size" form:"page_size"`
Page uint `json:"page" form:"page"`
} //FixPageAndSize
//修正分页的参数
func (p *Pagination) FixPageAndSize() {
if p.PageSize == 0 {
p.PageSize = 10
}
if p.Page == 0 {
p.Page = 1
}
}

handler/helper.go

package handler

import (
"bytes"
"crypto/md5"
"encoding/hex"
"encoding/xml"
"errors"
"fmt"
"github.com/gin-gonic/gin"
"io/ioutil"
"net/http"
"os"
"os/exec"
"pms/model"
"pms/util/log"
"reflect"
"regexp"
"strconv"
) const (
StatusOK uint = 200
StatusAuthError = 300
StatusError = 400
) func jsonError(c *gin.Context, status uint, msg string) {
c.AbortWithStatusJSON(http.StatusOK, gin.H{"status": status, "msg": msg})
}
func jsonData(c *gin.Context, data interface{}) {
c.AbortWithStatusJSON(http.StatusOK, gin.H{"status": StatusOK, "data": data})
}
func jsonPagination(c *gin.Context, list interface{}, total uint, p model.Pagination) {
c.AbortWithStatusJSON(http.StatusOK, gin.H{"status": StatusOK, "data": list, "total": total, "page": p.Page, "page_size": p.PageSize})
} func jsonErrorAuth(c *gin.Context, status uint, msg string) {
c.AbortWithStatusJSON(http.StatusOK, gin.H{"status": status, "msg": msg})
}
func handleError(c *gin.Context, err error) bool {
if err != nil {
jsonError(c, StatusError, err.Error())
return true
}
return false
}
func handleErrorAuth(c *gin.Context, err error) bool {
if err != nil {
jsonErrorAuth(c, StatusAuthError, err.Error())
return true
}
return false
}

之前练手使用基于gin的go web项目的更多相关文章

  1. 基于gin的golang web开发:集成swagger

    在前后端分离的项目维护一份完整且及时更新的api文档会极大的提高我们的工作效率,传统项目中接口文档都是由后端开发手写的,这种文档很难保证及时性,久而久之便失去了参考意义.swagger给我们提供了一种 ...

  2. 基于gin的golang web开发:路由

    Gin是一个用Golang编写的HTTP网络框架.它的特点是类似于Martini的API,性能更好.在golang web开发领域是一个非常热门的web框架. 启动一个Gin web服务器 使用下面的 ...

  3. 基于gin的golang web开发:路由二

    在基于gin的golang web开发:路由中我们介绍了Gin的路由和一些获取链接中参数的方法,本文继续介绍其他获取参数的方法. 文件上传 在web开发中文件上传是一个很常见的需求,下面我们来看一下基 ...

  4. 基于gin的golang web开发:模型绑定

    在前两篇文章介绍路由的时候,我们了解到gin可用通过类似DefaultQuery或DefaultPostForm等方法获取到前端提交过来的参数.参数不多的情况下也很好用,但是想想看,如果接口有很多个参 ...

  5. 基于gin的golang web开发:模型验证

    Gin除了模型绑定还提供了模型验证功能.你可以给字段指定特定的规则标签,如果一个字段用binding:"required"标签修饰,在绑定时该字段的值为空,那么将返回一个错误.开发 ...

  6. 基于gin的golang web开发:访问mysql数据库

    web开发基本都离不开访问数据库,在Gin中使用mysql数据库需要依赖mysql的驱动.直接使用驱动提供的API就要写很多样板代码.你可以找到很多扩展包这里介绍的是jmoiron/sqlx.另外还有 ...

  7. 基于gin的golang web开发:使用数据库事务

    在前文介绍访问数据库时介绍了github.com/jmoiron/sqlx包,本文基于这个包使用数据库事务. defer 在使用数据库事务之前,首先需要了解go语言的defer关键字.defer是go ...

  8. 基于gin的golang web开发:mysql增删改查

    Go语言访问mysql数据库需要用到标准库database/sql和mysql的驱动.标准库的Api使用比较繁琐这里再引入另一个库github.com/jmoiron/sqlx. go get git ...

  9. 基于gin的golang web开发:中间件

    gin中间件(middleware)提供了类似于面向切面编程或路由拦截器的功能,可以在请求前和请求之后添加一些自定义逻辑.实际开发中有很多场景会用到中间件,例如:权限验证,缓存,错误处理,日志,事务等 ...

  10. 基于gin的golang web开发:永远不要相信用户的输入

    作为后端开发者我们要记住一句话:"永远不要相信用户的输入",这里所说的用户可能是人,也可能是另一个应用程序."永远不要相信用户的输入"是安全编码的准则,也就是说 ...

随机推荐

  1. [转帖]一个小操作,SQL 查询速度翻了 1000 倍

    https://tidb.net/book/tidb-monthly/2022/2022-04/usercase/sql-1000 背景介绍​ 某一天早上来到公司,接到业务同学反馈,线上某个SQL之前 ...

  2. [转帖]Jmeter性能测试:高并发分布式性能测试

    一.为什么要进行分布式性能测试 当进行高并发性能测试的时候,受限于Jmeter工具本身和电脑硬件的原因,无法满足我们对大并发性能测试的要求.基于这种场景下,我们就需要采用分布式的方式来实现我们高并发的 ...

  3. [转帖]深入理解mysql-第十章 mysql查询优化-Explain 详解(上)

    目录 一.初识Explain 二.执行计划-table属性 三.执行计划-id属性 四.执行计划-select_type属性 一条查询语句在经过MySQL查询优化器的各种基于成本和规则的优化会后生成一 ...

  4. 周末拾遗 xsos 的学习与使用

    周末拾遗 xsos 的学习与使用 摘要 周末陪儿子上跆拳道课. 自己一个人傻乎乎的开着笔记本想着学习点东西. 上午看到了一个sosreport的工具. 本来想学习一下. 发现xsos 应该是更好的一个 ...

  5. [转帖]模拟enq: TX - row lock contention争用

    https://www.modb.pro/db/623036 enq: TX - row lock contention它表示一个事务正在等待另一个事务释放被锁定的行.这种等待事件通常发生在并发访问数 ...

  6. [转帖]Redis如何绑定CPU

    文章系转载,便于分类和归纳,源文地址:https://www.yisu.com/zixun/672271.html 绑定 CPU Redis 6.0 开始支持绑定 CPU,可以有效减少线程上下文切换. ...

  7. [转帖]ls命令

    ls(list) 命令可以说是Linux下最常用的命令之一 #ls -l;列出文件的详细信息 #ll 以上两个命令一样,ll是ls -l的简写 #ls -al;列出目录下的所有文件,包括以 . 开头的 ...

  8. 分布式事务和Spanner分布式数据库

    一.分布式事务 首先事务可以这么理解:程序员有一些不同的操作,或许针对数据库不同的记录,他们希望所有这些操作作为一个整体,不会因为失败而被分割,也不会被其他活动看到中间状态.事务处理系统要求程序员对这 ...

  9. golang 中使用 writev (sendmsg) 系统调用来一次发送多块数据

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 writev,或者说 sendmsg 等系统调用,能够发送 ...

  10. 【解决了一个小问题】macbook m1上的docker build问题

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 1. docker hub限制的问题 因为docker b ...