用gin写简单的crud后端API接口

news2025/1/23 4:09:41

提要

使用gin框架(go的web框架)来创建简单的几个crud接口)
使用技术: gin + sqlite3 + sqlx

创建初始工程

新建文件夹,创建三个子文件夹

分别初始化工程 go mod

如果没有.go文件,执行go mod tidy可能报错(warning: "all" matched no packages), 可以先不弄,只初始化模块就行(go mod init 模块名)
# 项目根目录创建模块
go mod init go_manager 
go mod tidy
# 进入db目录
cd db
# 初始化模块
go mod init go_manager_db 
go mod tidy
# 进入utils目录
cd ../utils
# 初始化模块
go mod init go_manager_utils 
go mod tidy
# 进入web目录
cd ../web
# 初始化模块
go mod init go_manager_web 
go mod tidy

go_manager_db模块编写

创建数据库连接(sqlite如果没有库会自动建)

// db\main.go
package go_manager_db

import (
    "fmt"
    "github.com/jmoiron/sqlx"
    _ "github.com/mattn/go-sqlite3"
)

// 数据库相关操作
var db *sqlx.DB

// 初始化数据库连接
func InitDB() (err error) {
    dsn := "./manager.db"
    // 连接
    // Open可能仅校验参数,而没有与db间创建连接,
    // 要确认db是否可用,需要调用Ping。Connect则相当于Open+Ping。
    db, err = sqlx.Connect("sqlite3", dsn)
    if err != nil {
        fmt.Printf("connect DB failed, err:%v\n", err)
        return
    }
    // 最大连接数
    db.SetMaxOpenConns(100)
    // 最大空闲连接数
    db.SetMaxIdleConns(16)
    // 初始化方法,建表+插入原始数据
    CreateRoleTable()
    CreateUserTable()
    return
}

添加建表方法(初始化权限表和用户表)

// db\main.go
package go_manager_db

import (
    "fmt"
    "github.com/jmoiron/sqlx"
    _ "github.com/mattn/go-sqlite3"
)

// 数据库相关操作
var db *sqlx.DB

// 初始化数据库连接
func InitDB() (err error) {......}
// 创建用户表
func CreateUserTable() error {
    sqlc := `
    CREATE TABLE IF NOT EXISTS "mal_user" (
      -- sqlite 不能用 comment 添加注释
      "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT , -- '主键'
      "uname" varchar(20) NOT NULL UNIQUE , -- '用户昵称'
      "upass" varchar(50) NOT NULL, -- '密码(md5加密)'
      "rid" INTEGER NOT NULL UNIQUE DEFAULT 1 -- '角色id'
    ); 
    `
    _, err := db.Exec(sqlc)
    if err != nil {
        fmt.Println(err)
        return err
    }
    // 初始化表
    //因为有unique约束,所以不会重复添加
    // sqlStr := "insert into mal_user(uname,upass,rid) values(?,?,?)"
    Insert("mal_user", []string{"uname", "upass", "rid"}, "admin", "e120012d113ff6ea124a2493453c6dd5", 2)
    return nil
}

// 创建权限表
func CreateRoleTable() error {
    sqlc := `
    CREATE TABLE IF NOT EXISTS "mal_role" (
      "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, -- '主键' 
      "role" varchar(20) NOT NULL UNIQUE DEFAULT 'user' -- '角色(权限)' 
    ); 
    `
    _, err := db.Exec(sqlc)
    if err != nil {
        return err
    }
    // 初始化表
    // 因为有unique约束,所以不会重复添加 
    // 有四种权限,id(自增)越大代表权限越大,root>super>admin>user
    Insert("mal_role", []string{"role"}, "user")
    Insert("mal_role", []string{"role"}, "admin")
    Insert("mal_role", []string{"role"}, "super")
    Insert("mal_role", []string{"role"}, "root")
    return nil
}

base.go: 通用插入和删除方法

// db\base.go
package go_manager_db

import (
    "fmt"
    _ "github.com/mattn/go-sqlite3"
    utils "go_manager_utils"
)

// 插入数据
func Insert(tableName string, params []string, datas ...interface{}) (err error) {
    // 拼接 表名(参数1,参数2,...)
    paramStr := utils.ParamsStr(params)
    // 拼接 values(?,?,...)
    values := utils.ValueStr(len(params))
    var sqlStr = "insert into " + tableName + paramStr + " values" + values
    fmt.Println(sqlStr)
    _, err = db.Exec(sqlStr, datas...) // 要用...展开
    if err != nil {
        fmt.Println(err)
        fmt.Println("插入数据失败")
        return
    }
    return
}

// 删除数据
func Delete(tableName string, id int64) (err error) {
    sqlStr := "delete from " + tableName + " where id=?"
    fmt.Println(sqlStr)
    _, err = db.Exec(sqlStr, id)
    if err != nil {
        fmt.Println("删除数据失败")
        return
    }
    return
} 

model.go: 定义数据表对应的结构体

package go_manager_db
 
// 专门定义与数据库交互的结构体

// 用户表
type MalUser struct {
    Id    int64  `db:"id" json:"Rd"`
    Uname string `db:"uname" json:"Uname"`
    Upass string `db:"upass" json:"Upass"`
    Rid   int64  `db:"rid" json:"Rid"`
}
// 角色表
type MalRole struct {
    Id   int64  `db:"id" json:"Id"`
    Role string `db:"role" json:"Role"`
}

mal_user.go和mal_role.go: 定义用户表和角色表的crud方法

mal_user.go

package go_manager_db

import (
    "fmt" 
    utils "go_manager_utils"

    _ "github.com/mattn/go-sqlite3"
)

// 查数据
func GetAllUser() (users []*MalUser, err error) {
    sqlStr := `select * from mal_user`
    // 查询,记录到booklist
    err = db.Select(&users, sqlStr)
    if err != nil {
        fmt.Println("查询信息失败")
        fmt.Println(err)
        return
    }
    return
}

// 根据id查数据
func GetUserById(id int64) (user MalUser, err error) {
    // 如果返回的是指针,需要初始化
    //book=&Book{}
    sqlStr := "select * from mal_user where id=?"
    err = db.Get(&user, sqlStr, id)
    if err != nil {
        fmt.Println("查询信息失败")
        return
    }
    return
}

// 根据name查数据
func GetUserByName(uname string, upass string) (user MalUser, err error) {
    sqlStr := "select * from mal_user where uname=? and upass=?"
    err = db.Get(&user, sqlStr, uname, upass)
    if err != nil {
        fmt.Println("查询信息失败")
        return
    }
    return
}

// 根据id改
func UptUserById(uid string, params []string, datas ...interface{}) (err error) {
    // 拼接参数列表 xxx=?,xxx=?
    paramsStr := utils.UptParamsStr(params)
    // uid直接传字符串拼接
    sqlStr := "update mal_role set " + paramsStr + " where id=" + uid
    _, err = db.Exec(sqlStr, datas...)
    if err != nil {
        fmt.Println("修改信息失败")
        return
    }
    return
}

mal_role.go

package go_manager_db

import (
    "fmt"
    _ "github.com/mattn/go-sqlite3"
)

// 应该id越大,权限越高,比较方便区分权限
// user < admin < super < root
// 查数据
func GetAllRole() (roles []*MalRole, err error) {
    sqlStr := `select * from mal_role`
    // 查询,记录到booklist
    err = db.Select(&roles, sqlStr)
    if err != nil {
        fmt.Println("查询信息失败")
        fmt.Println(err)
        return
    }
    return
}

// 根据id查数据
func GetRoleById(id int64) (role MalRole, err error) {
    // 如果返回的是指针,需要初始化
    //book=&Book{}
    sqlStr := "select * from mal_role where id=?"
    err = db.Get(&role, sqlStr, id)
    if err != nil {
        fmt.Println("查询信息失败")
        return
    }
    return
}

// 根据id改数据
func UptRoleById(id int64, roleName string) (err error) {
    // 如果返回的是指针,需要初始化
    //book=&Book{}
    sqlStr := "update mal_role set role=? where id=?"
    _, err = db.Exec(sqlStr, roleName, id)
    if err != nil {
        fmt.Println("修改信息失败")
        return
    }
    return
}

引入项目里的其他模块: utils

在go.mod末尾添加

replace go_manager_utils => ../utils

运行 go mod tidy

go_manager_utils模块编写

jwt.go: 编写加密方法,定时销毁token方法

package go_manager_util

import (
    "crypto/md5"
    "fmt"
    "gopkg.in/square/go-jose.v2"
    "gopkg.in/square/go-jose.v2/jwt"
    "time"
)

// sign 签名
// 传入密码,加密
func SignJWT(secret string, uname string, upass string) (jwtStr string) {
    key := []byte(secret)
    fmt.Println(secret)
    sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: key},
        (&jose.SignerOptions{}).WithType("JWT"))
    if err != nil {
        panic(err)
    }

    cl := jwt.Claims{
        // Registered claims : 这里有一组预定义的声明,它们不是强制的,但是推荐
        // 比如:iss (issuer), exp (expiration time), sub (subject), aud (audience)等。
        Issuer:    uname,
        Subject:   upass,
        NotBefore: jwt.NewNumericDate(time.Now()),
        Audience:  jwt.Audience{"name", "admin"},
    }
    raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize()
    if err != nil {
        panic(err)
    }

    // fmt.Println(raw)
    return raw
}

// 解析jwt
// 传入key(之前加密的密码),raw(jwt令牌)
func ParseJWT(key string, raw string) {
    var sharedKey = []byte(key)
    tok, err := jwt.ParseSigned(raw)
    if err != nil {
        panic(err)
    }
    out := jwt.Claims{}
    // 解析出issuer(uname)和subject(upass),校验
    if err := tok.Claims(sharedKey, &out); err != nil {
        panic(err)
    }
    fmt.Printf("iss: %s, sub: %s\n", out.Issuer, out.Subject)
}

// DM5加密
func MD5(str string) string {
    data := []byte(str) //切片
    has := md5.Sum(data)
    md5str := fmt.Sprintf("%x", has) //将[]byte转成16进制
    return md5str
}

// 销毁TokenMap的方法
// 定时销毁token(默认2小时)
func DestoryTokenMap(tokenMap map[string]string) {
    for k := range tokenMap {
        delete(tokenMap, k)
    }
}

myTime.go: 定义定时器方法

package go_manager_util

import ( 
    "time"
)

// 定义函数类型
type Fn func() error

// 定时器中的成员
type MyTicker struct {
    MyTick *time.Ticker
    Runner Fn
}
type MyTimer struct {
    MyTime *time.Timer
    Runner Fn
}

func NewMyTick(interval int, f Fn) *MyTicker {
    return &MyTicker{
        MyTick: time.NewTicker(time.Duration(interval) * time.Second),
        Runner: f,
    }
}

// 一次性
func NewMyTimer(interval int, f Fn) *MyTimer {
    return &MyTimer{
        MyTime: time.NewTimer(time.Duration(interval) * time.Second),
        Runner: f,
    }
}

// 启动定时器需要执行的任务
func (t *MyTicker) Start() {
    for {
        select {
        case <-t.MyTick.C:
            t.Runner()
        }
    }
}

// 启动定时器需要执行的任务
func (t *MyTimer) Start() { 
    select {
    case <-t.MyTime.C:
        t.Runner()
    } 
}

// func over() error {
//     fmt.Println("token过期")
//     return nil
// }
// 测试
// func main() {
//     t := NewMyTimer(2, over)
//     t.Start()
// }

res.go: 响应前端请求的方法

package go_manager_util

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "net/http"
)

/* 通用响应方法 */
func R(c *gin.Context, err error, msg interface{}, data interface{}) {
    // 如果有err,就说明是有错误,就返回错误响应(msg)
    if err != nil {
        fmt.Println(err)
        c.JSON(http.StatusInternalServerError, gin.H{
            "status": 500,
            "msg":    msg,
        })
        return
    }
    // 返回正确响应(data)
    c.JSON(http.StatusOK, gin.H{
        "status": 200,
        "msg":    data,
    })
}

stringUtils.go: 封装字符串操作方法

package go_manager_utils

// 拼接sql语句的value
// len是语句有几个参数
func ValueStr(len int) (values string) {
    // 拼接 values(?,?,...)
    values = "("
    for i := 0; i < len-1; i++ {
        values += "?"
        values += ","
    }
    values += "?"
    values += ")"
    return
}

// 拼接sql语句update的param
// params是参数名数组
func UptParamsStr(params []string) (paramStr string) {
    // 拼接参数列表 xxx=?,xxx=?
    paramStr = ""
    for i := 0; i < len(params)-1; i++ {
        paramStr += params[i]
        paramStr += "=?,"
    }
    paramStr += params[len(params)-1]
    paramStr += "=?"
    return
}

// 拼接sql语句的param
// params是参数名数组
func ParamsStr(params []string) (paramStr string) {
    // 拼接 表名(参数1,参数2,...)
    paramStr = "("
    for i := 0; i < len(params)-1; i++ {
        paramStr += params[i]
        paramStr += ","
    }
    paramStr += params[len(params)-1]
    paramStr += ")"
    return
}

运行go mod tidy处理go文件里的依赖

go_manager_web模块编写

main.go: 主要逻辑,创建web实例,注册路由...

package go_manager_web

import (
    "fmt"
    "github.com/gin-gonic/gin"
    db "go_manager_db"
    utils "go_manager_utils"
    "net/http"
)

// 定义路由组
// 组中组(嵌套路由组)
func DefineRouteGroup(fatherGroup *gin.RouterGroup, groupName string, r *gin.Engine) *gin.RouterGroup {
    var group *gin.RouterGroup
    if fatherGroup != nil {
        // v1/groupName
        group = fatherGroup.Group(groupName)
    } else {
        // /groupName
        group = r.Group(groupName)
    }
    // 返回路由组
    return group
}

// 存放 token (不同ip不同token)
var TokenMap = make(map[string]string, 10)

// 定时销毁token
func timeDT() {
    // 两小时后销毁
    t := utils.NewMyTimer(2*60*60, func() error {
        utils.DestoryTokenMap(TokenMap)
        return nil
    })
    t.Start()
    fmt.Println(TokenMap)
}

// 路由和处理函数放在不同文件好像会使中间件失效
func Login(c *gin.Context) { 
    user := db.MalUser{}
    // 绑定json和结构体(接收json,数据放入结构体)
    if err := c.BindJSON(&user); err != nil {
        return
    }
    uname := user.Uname
    upass := user.Upass 
    userModel, err := db.GetUserByName(uname, upass)
    if err != nil || &userModel == nil {
        fmt.Println(err)
        c.JSON(500, gin.H{
            "status": 500,
            "msg":    "登录失败",
        })
        return
    } 
    token := utils.SignJWT("malred", uname, upass)
    // 存入map
    // fmt.Println(c.ClientIP(),c.RemoteIP())
    TokenMap[c.ClientIP()] = token
    fmt.Println(TokenMap)
    c.JSON(http.StatusOK, gin.H{
        "status": 200,
        "msg":    "登录成功",
        // 返回jwt令牌(密码因为前端md5加密过,所以直接放入jwt)
        "token": token,
    })
    go timeDT()
}

// 路由器
// 启动默认的路由
var r = gin.Default()

// user路由组
var v1 *gin.RouterGroup

func Run() {
    // 使用中间件
    // 日志
    r.Use(gin.Logger())
    // 错误恢复
    r.Use(gin.Recovery())
    // 跨域
    r.Use(Core())
    // 阻止缓存响应
    r.Use(NoCache())
    // 安全设置
    r.Use(Secure())
    // 创建路由组v1
    v1 = DefineRouteGroup(nil, "v1", r)
    v1.POST("login", Login)
    // 注册user的路由
    registerUser(Token(), Core())
    // 注册role的路由
    registerRole(Token(), Core())
    // 启动webserver,监听本地127.0.0.1(默认)端口
    r.Run(":10101")
}

moddilewares.go: 中间件

package go_manager_web

import ( 
    utils "go_manager_utils"
    "net/http"
    "strconv"
    "time"

    "github.com/gin-gonic/gin"
)

//解决跨域问题
func Core() gin.HandlerFunc {
    return func(c *gin.Context) {
        method := c.Request.Method
        c.Header("Access-Control-Allow-Origin", "*")
        c.Header("Access-Control-Allow-Headers", "*")
        c.Header("Access-Control-Allow-Methods", "*")
        c.Header("Access-Control-Expose-Headers", "Content-Length,Access-Control-Allow-Origin,Access-Control-Allow-Headers,Content-Type")
        c.Header("Access-Control-Max-Age", "3600")
        c.Header("Access-Control-Allow-Credentials", "true")
        //放行索引options
        if method == "OPTIONS" {
            c.AbortWithStatus(http.StatusNoContent)
        }
        //处理请求
        c.Next()
    }
}

// 权限认证(验证token)
func Token() gin.HandlerFunc {
    return func(c *gin.Context) {
        // for k, v := range c.Request.Header {
        //     fmt.Println(k, v)
        // }
        secret := c.Request.Header["Secret"] // 获取前端传来的secret
        token := c.Request.Header["Token"]
        if len(token) == 0 {
            // 验证不通过,不再调用后续的函数处理
            c.Abort()
            c.JSON(http.StatusUnauthorized, gin.H{
                "code":    401,
                "message": "访问未授权",
            })
            return
        }
        timeInt64 := strconv.FormatInt(time.Now().UnixNano()/1e6/1000/60, 10)
        md5Str := utils.MD5(timeInt64 + TokenMap[c.ClientIP()])
        // fmt.Println(TokenMap[c.ClientIP()], timeInt64)
        // fmt.Println(timeInt64 + TokenMap[c.ClientIP()])
        // fmt.Println(md5Str, secret[0])
        if md5Str != secret[0] {
            // 验证不通过,不再调用后续的函数处理
            c.Abort()
            c.JSON(http.StatusUnauthorized, gin.H{
                "code":    401,
                "message": "访问未授权",
            })
            return
        }
        // 验证jwt
        // utils.ParseJWT(secret[0][8:11]+secret[0][19:22], token[0])
        //处理请求
        c.Next()
    }
}

// 阻止缓存响应
func NoCache() gin.HandlerFunc {
    return func(ctx *gin.Context) {
        ctx.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")
        ctx.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")
        ctx.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
        ctx.Next()
    }
}

// 响应 options 请求, 并退出
// func Options() gin.HandlerFunc {
//     return func(ctx *gin.Context) {
//         if ctx.Request.Method != "OPTIONS" {
//             ctx.Next()
//         } else {
//             ctx.Header("Access-Control-Allow-Origin", "*")
//             ctx.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
//             ctx.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
//             ctx.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
//             ctx.Header("Content-Type", "application/json")
//             ctx.AbortWithStatus(200)
//         }
//     }
// }

// 安全设置
func Secure() gin.HandlerFunc {
    return func(ctx *gin.Context) {
        ctx.Header("Access-Control-Allow-Origin", "*")
        ctx.Header("X-Frame-Options", "DENY")
        ctx.Header("X-Content-Type-Options", "nosniff")
        ctx.Header("X-XSS-Protection", "1; mode=block")
        if ctx.Request.TLS != nil {
            ctx.Header("Strict-Transport-Security", "max-age=31536000")
        }

        // Also consider adding Content-Security-Policy headers
        // ctx.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com")
    }
}

// todo 权限控制(token携带当前用户的权限信息,过滤低于指定权限的请求)

role.go和user.go: 真的role和user表的web操作

role.go

package go_manager_web

import (
    "fmt"
    db "go_manager_db"
    utils "go_manager_utils"
    "strconv" 
    "github.com/gin-gonic/gin"
)

func GetAllRoleHandler(c *gin.Context) {
    roles, err := db.GetAllRole()
    // 通用响应
    utils.R(c, err, "获取角色列表失败", roles)
}
func AddRoleHandler(c *gin.Context) {
    // Role := c.PostForm("Role")
    // fmt.Println(Role)
    role := db.MalRole{}
    //绑定json和结构体
    if err := c.BindJSON(&role); err != nil {
        return
    }
    Role := role.Role
    err := db.Insert("mal_role", []string{"role"}, Role)
    // 通用响应
    utils.R(c, err, "添加角色失败", "添加角色成功")
}
func DelRoleHandler(c *gin.Context) {
    // 从url获取参数
    idStr := c.Query("rid")
    // fmt.Println(idStr)
    rid, err := strconv.ParseInt(idStr, 10, 64)
    err = db.Delete("mal_role", rid)
    // 通用响应
    utils.R(c, err, "删除角色失败", "删除角色成功")
}
func GetOneRoleHandler(c *gin.Context) {
    // 从url获取参数
    idStr := c.Query("rid")
    fmt.Println(idStr)
    rid, _ := strconv.ParseInt(idStr, 10, 64)
    one, err2 := db.GetRoleById(rid)
    // 通用响应
    utils.R(c, err2, "查询角色失败", one)
}
func UptRoleHandler(c *gin.Context) {
    role := db.MalRole{}
    //绑定json和结构体
    if err := c.BindJSON(&role); err != nil {
        return
    } 
    rid := role.Id
    roleName := role.Role
    fmt.Println(role)
    err := db.UptRoleById(rid, roleName)
    // 通用响应
    utils.R(c, err, "修改角色失败", "修改角色成功")
}
func registerRole(middles ...gin.HandlerFunc) {
    // 创建路由组v1/user
    role := DefineRouteGroup(v1, "role", r)
    // 添加中间件
    if middles != nil {
        role.Use(middles...)
    }
    // 获取所有
    role.GET("all", GetAllRoleHandler)
    // 添加
    role.POST("add", AddRoleHandler)
    // 删除
    role.DELETE("del", DelRoleHandler)
    // 根据id获取
    role.GET("id", GetOneRoleHandler)
    // 根据id修改
    role.PUT("upt", UptRoleHandler)
}

user.go

package go_manager_web

import (
    "fmt"
    db "go_manager_db"
    utils "go_manager_utils"
    "strconv" 

    "github.com/gin-gonic/gin"
)

func GetAllUserHandler(c *gin.Context) {
    users, err := db.GetAllUser()
    // 通用响应
    utils.R(c, err, "查询角色失败", users)
}
func AddUserHandler(c *gin.Context) {
    // uname := c.PostForm("uname")
    // upass := c.PostForm("upass")
    // idStr := c.PostForm("rid")
    user := db.MalUser{}
    //绑定json和结构体
    if err := c.BindJSON(&user); err != nil {
        return
    }
    uname := user.Uname
    upass := user.Upass
    rid := user.Rid
    fmt.Println(user)
    // rid, err := strconv.ParseInt(idStr, 10, 64)
    err := db.Insert("mal_user", []string{"uname", "upass", "rid"}, uname, upass, rid)
    // 通用响应
    utils.R(c, err, "添加角色失败", "添加角色成功")
}
func DelUserHandler(c *gin.Context) {
    // 从url获取参数
    idStr := c.Query("uid")
    // fmt.Println(idStr)
    uid, err := strconv.ParseInt(idStr, 10, 64)
    err = db.Delete("mal_user", uid)
    // 通用响应
    utils.R(c, err, "删除角色失败", "删除角色成功")
}
func GetOneUserHandler(c *gin.Context) {
    // 从url获取参数
    idStr := c.Query("uid")
    fmt.Println(idStr)
    uid, _ := strconv.ParseInt(idStr, 10, 64)
    one, err2 := db.GetUserById(uid)
    // 通用响应
    utils.R(c, err2, "查询角色失败", one)
}
func UptUserHandler(c *gin.Context) {
    // 从url获取参数
    // uid := c.PostForm("uid")
    // uname := c.PostForm("uname")
    // upass := c.PostForm("upass")
    // ridStr := c.PostForm("rid")
    user := db.MalUser{}
    //绑定json和结构体
    if err := c.BindJSON(&user); err != nil {
        return
    }
    uname := user.Uname
    upass := user.Upass
    rid := user.Rid
    uid := user.Id
    // fmt.Println(idStr, UserName)
    // rid, _ := strconv.ParseInt(ridStr, 10, 64)
    err := db.UptUserById(strconv.FormatInt(uid, 10), []string{"uname", "upass", "rid"}, uname, upass, rid)
    // 通用响应
    utils.R(c, err, "修改角色失败", "修改角色成功")
}
func registerUser(middles ...gin.HandlerFunc) {
    // 创建路由组v1/user
    user := DefineRouteGroup(v1, "user", r)
    // 添加中间件
    if middles != nil {
        user.Use(middles...)
    }
    user.GET("all", GetAllUserHandler)
    // 添加
    user.POST("add", AddUserHandler)
    // 删除
    user.DELETE("del", DelUserHandler)
    // 根据id获取
    user.GET("id", GetOneUserHandler)
    // 根据id修改
    user.PUT("upt", UptUserHandler)
}

运行go mod tidy

忘了,要引用项目里的其他包

replace go_manager_utils => ../utils
replace go_manager_db => ../db

go mod tidy

编写根目录的go_manager模块

main.go

package main

import ( 
    db "go_manager_db"
    web "go_manager_web"
)

func main() {
    // 初始化数据库
    db.InitDB()   
    // 开启服务
    web.Run()
}

go.mod

module go_manager

go 1.18

replace go_manager_web => ./web

replace go_manager_db => ./db

replace go_manager_utils => ./utils

go mod tidy

测试(可以用go build打包)

完整目录结构

go run main.go

因为后端存的密码是md5加密过的,所以前端也要传md5加密的密码,二者相同才能通过

安全: 我的安全不咋地,加密的方法是前端根据当前时间戳(转为分钟,防止因为前后端延迟而导致时间戳不一致)+登录后从后端获取的token来md5,每次请求都会验证这个md5(后端也加密(时间戳/60+token)然后对比),这个就不测试了

代码仓库:

https://gitee.com/malguy/go-manager

配套前端管理系统(react18):

https://github.com/malred/base-manager

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/397175.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GreenPlum小结

什么是GreenPlum&#xff1f;GreenPlum是业界最快最高性价比的关系型分布式数据库,它在开源的PostgreSQL的基础上采用MPP架构&#xff08;Massive Parallel Processing&#xff0c;海量并行处理&#xff09;,具有强大的大规模数据分析任务处理能力。GreenPlum作为大数据融合存储…

【UE4 RTS游戏】03-摄像机运动_旋转视角

效果可以通过WASD控制“CameraPawn”的移动&#xff1b;通过鼠标中键旋转视角&#xff1b;通过alt鼠标中键将视角回归默认值&#xff1b;通过shift加速移动。步骤打开“CameraPawnController”&#xff0c;给如下节点添加注释&#xff0c;命名为“MovementX”接下来开始开始编辑…

JDK解压安装及idea开发工具配置

1. 安装JDK 1.1 下载安装包 下载安装包&#xff0c;直接解压&#xff0c;注意&#xff0c;解压的路径不要有中文 1.2 配置环境变量 右键点击我的电脑&#xff0c;选择属性 选择高级系统设置 选择环境变量 选择新建 在变量名中输入JAVA_HOME&#xff0c;变量值就是1.1中压缩包…

Windows环境下实现设计模式——访问者模式(JAVA版)

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天总结一下Windows环境下如何编程实现访问者模式&#xff08;设计模式&#xff09;。不知道大家有没有这样的感觉&#xff0c;看了一大堆编程和设计模式的书&#xff0c;却还是很难理解设计模式&#xff0c…

【C++】类和对象(收尾)

文章目录成员变量初始化问题初始化列表explicit关键字static成员特性&#xff1a;友元友元函数友元类内部类特性匿名对象成员变量初始化问题 在创建对象时&#xff0c;编译器通过调用构造函数&#xff0c;给了对象中各个成员变量一个合适的初始值。但是这并不能够称为对对象中成…

简单了解蓄电池在直流系统中的使用现状!

一般情况下&#xff0c;由市电通过直流配电屏为变电站的直流系统提供工作电源&#xff0c;包括对蓄电池组进行饱和和充电使蓄电池处于备用状态&#xff0c;当交流失电或系统需要进行大电流供电时&#xff0c;蓄电池需要迅速切入&#xff0c;向事故负荷、自动装置、保护装置以及…

本地套接字

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起探讨和分享Linux C/C/Python/Shell编程、机器人技术、机器学习、机器视觉、嵌入式AI相关领域的知识和技术。 本地套接字专栏&#xff1a;《Linux从小白到大神》《网络编程》 本地套接字通信需要一个文件&#xff…

tensorflow【import transformers 报错】

目录 一、安装 安装好了tensorflow,但是import时候报错&#xff1a; import transformers 报错 一、安装 &#xff08;1&#xff09;创建环境&#xff1a; conda create -n [name] python3.3-3.7 &#xff08;2&#xff09;激活环境&#xff1a; conda activate [name] …

Python中赋值、引用、深浅拷贝的区别和联系

文章目录一、对象的唯一id二、赋值三、可变对象和不可变对象四、函数的参数传递五、深拷贝和浅拷贝六、举个栗子6.1 不可变对象的拷贝6.2 可变对象的拷贝6.3 可变对象改变外层元素6.4 可变对象改变内层元素七、总结一、对象的唯一id python中的所有对象都有自己的唯一id&#…

典型回溯题目 - 全排列(一、二)

典型回溯题目 - 全排列&#xff08;一、二&#xff09; 46. 全排列 题目链接&#xff1a;46. 全排列状 题目大意&#xff1a; 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 注意&#xff1a;&#xff08;1&#xf…

Linux命令·which·whereis·locate·find

我们经常在linux要查找某个文件&#xff0c;但不知道放在哪里了&#xff0c;可以使用下面的一些命令来搜索&#xff1a; which 查看可执行文件的位置。whereis 查看文件的位置。 locate 配合数据库查看文件位置。find 实际搜寻硬盘查询文件名称。whichwhich命令的作用是&#x…

DJ1-1 操作系统引论

目录 一、操作系统的概念 二、操作系统的目标 三、操作系统的作用 一、操作系统的概念 定义一 操作系统是一组控制和管理计算机软硬件资源、合理地对各类作业进行调度以及方便用户使用的程序集合。 定义二 操作系统是位于硬件层&#xff08;HAL&#xff09;之上&#xff…

SQL 基础函数,通配符,BETWEEN ,用法复习

使用 SQL _ 通配符 下面的 SQL 语句选取 name 以一个任意字符开始&#xff0c;然后是 “oogle” 的所有客户&#xff1a; SELECT * FROM Websites WHERE name LIKE _oogle;下面的 SQL 语句选取 name 以 “G” 开始&#xff0c;然后是一个任意字符&#xff0c;然后是 “o”&am…

看完这篇我不信你不会二叉树的层序遍历【C语言】

目录 实现思路 代码实现 之前介绍了二叉树的前、中、后序三种遍历&#xff0c;采用的是递归的方式。今天我们来学习另外一种遍历方式——层序遍历。层序遍历不容小觑&#xff0c;虽然实现方法并不难&#xff0c;但是它所采取的思路是很值得学习的&#xff0c;与前三者不同&am…

学习笔记-架构的演进之容器的封装-3月day06

文章目录前言封装应用的Dockerwhy Docker not LXC?附前言 当文件系统、访问、资源都可以被隔离后&#xff0c;容器就已经具备它降生所需要的全部前置支撑条件了。为了降低普通用户综合使用 namespaces、cgroups 这些低级特性的门槛&#xff0c;2008 年 Linux Kernel 2.6.24 内…

Java中的final和权限修饰符

目录 final 常量 细节&#xff1a; 权限修饰符 Java权限修饰符用于控制类、方法、变量的访问范围。Java中有四种权限修饰符&#xff1a; 权限修饰符的使用场景&#xff1a; final 方法 表明该方法是最终方法&#xff0c;不能被重写。类 表明该类是最终类&#xff0c;不能被继…

Jetpack太香了,让开发效率提升了不少

作者&#xff1a;Jingle_zhang 第三方App使用Jetpack等开源框架非常流行&#xff0c;在Gradle文件简单指定即可。然而ROM内置的系统App在源码环境下进行开发&#xff0c;与第三方App脱节严重&#xff0c;采用开源框架的情况并不常见。但如果系统App也集成了Jetpack或第三方框架…

【UE4 RTS游戏】04-摄像机运动_鼠标移动到视口边缘时移动Pawn

效果可以看到当鼠标移动到视口边缘时&#xff0c;Pawn就会向这个方向移动。步骤打开项目设置&#xff0c;添加两个操作映射打开“CameraPawnController”&#xff0c;在事件图表中添加两个浮点型变量&#xff0c;一个为公有一个为私有。分别命名为“ZoomSensitivity”、“MaxAr…

【Linux】帮助文档查看方法

目录1 Linux帮助文档查看方法1.1 man1.2 内建命令(help)1 Linux帮助文档查看方法 1.1 man man 是 Linux 提供的一个手册&#xff0c;包含了绝大部分的命令、函数使用说明。 该手册分成很多章节&#xff08;section&#xff09;&#xff0c;使用 man 时可以指定不同的章节来浏…

ubuntu 系统安装docker——使用docker打包python项目,整个流程介绍

目录 1 安装docker和配置镜像源 2 下载基础镜像 3 通过镜像创建容器 4 制作项目所需的容器 5 容器制作好后打包为镜像 6 镜像备份为.tar文件 7 从其他服务器上恢复镜像 8 docker的其他常用指令 首先科普一下镜像、容器和实例&#xff1b; 镜像&#xff1a;相当于安装包&…