gorm 操作数据库,类似java中的mybatis
defer 延迟
让程序其他逻辑先执行,defer定义的逻辑最后执行
recover 恢复
只在defer函数中有效
panic 恐慌
程序执行到panic的时候自动崩溃
示例:
defer func(){
if err := recover(); err !=nil{
fmt.Println("捕获异常:", err)
}
}()
defer fmt.Println(1)
defer fmt.Println(2)
panic("程序异常,这里主动崩溃中断")
1、获取url中的参数
在路由里修改路径 /user/:id ,然后再controller里面 id := c.Param("id") ,最后就可以取到参数啦
2、获取post请求中表单的参数
直接在controller里面获取 cid := c.PostForm("cid")
还有一种是可以设置默认值的 name := c.DefaultPostForm("name", "no people")
3、获取请求中以json封装的参数
3.1 、直接获取
param := make(map[string]interface{})
err := c.BindJSON(¶m)
if err== nil {
Success(c, 200, param["name"], param["cid"], 1)
return
}
Error(c, 500)
3.2、通过封装结构体方式
type Search struct{
Name string `json:"name"`
Cid int `json:"cid"`
}
search := & Search{}
err := c.BindJSON(&search)
if err==nil {
Success(c, 200, searche.Name, search.Cid, 1)
return
}
Error(c, 500)
用结构体 struct 定义返回的json格式
type JsonStruct struct{
Code int 'json:"code"'
Msg interface{} 'json:"msg"' //类型不确定用interface
Data interface{} 'json:"data"'
Count int64 'json:"count"'
}
//成功返回的json结构
func Success(c *gin.Context, code int, msg interface{}, data interface{}, count int64){
json := &JsonStruct{Code: code, Msg: msg, Data: data, Count: count}
c.json(200, json)
}
r.GET("/user/list", func(ctx *gin.Context){
//这里的func是用来处理路由请求成功后的业务逻辑
ctx.String(http.StatusOK, "查询成功")
})
路由分组:将同一类路由放到一起
user := r.Group("/user")
{
user.POST("/list", func(ctx *gin.Context){
ctx.String(http.StatusOK, "查到user list")
})
user.PUT("/add", func(ctx *gin.Context){
ctx.String(http.StatusOK, "添加用户成功")
})
}
Gin里面管GET POST 等叫路由啊!
main 入口包,一个系统只有一个main
go run hello.go
go build hello.go //编译成可执行文件,上线的时候只有执行该可执行文件就可以了
这块需要结合官方最新文档写。
和视频中的版本略有差异
这里贴出我的
gorm.io/driver/mysql v1.5.7
gorm.io/gorm v1.25.12
package dao import ( "go-ranking/config" "go-ranking/pkg/logger" "gorm.io/driver/mysql" "gorm.io/gorm" "time" ) var ( Db *gorm.DB err error ) func init() { Db, err = gorm.Open(mysql.Open(config.Mysqldb), &gorm.Config{}) // ----------------------- Connection Pool Settings ----------------------- // SetMaxIdleConns sets the maximum number of connections in the idle connection pool. sqlDB, err := Db.DB() if err != nil { logger.Error(map[string]interface{}{"database error": err.Error()}) return } sqlDB.SetMaxIdleConns(10) // SetMaxOpenConns sets the maximum number of open connections to the database. sqlDB.SetMaxOpenConns(100) // SetConnMaxLifetime sets the maximum amount of time a connection may be reused. sqlDB.SetConnMaxLifetime(time.Hour) }
安 gorm依赖
go get -u gorm.io/gorm
安装 mysql驱动
go get -u gorm.io/driver/mysql
package main import ( "fmt" ) func main() { fmt.Println("程序开始") safeFunction() fmt.Println("程序继续运行...") } func safeFunction() { defer func() { if r := recover(); r != nil { // 捕获 panic fmt.Println("捕获到异常:", r) } }() fmt.Println("执行函数") panic("触发异常") // 触发 panic,程序中断 fmt.Println("这行代码不会执行") }
思考:这里 err := context.BindJSON(&parms) 可以写成 err := context.BindJSON(parms) 吗? /* 答:不可以写成 err := context.BindJSON(parms),因为 BindJSON 需要一个指针类型的参数,即传入的参数必须是指向接收数据的变量的指针。 在 err := context.BindJSON(&parms) 中,&parms 是 parms 的指针,指向这个 map,所以 BindJSON 可以直接修改 parms 的内容,将请求中的 JSON 数据解析并赋值到 parms 中。如果写成 context.BindJSON(parms),则传入的是 map 值的副本,BindJSON 将无法正确解析并赋值给 parms。 为什么 BindJSON 需要指针? BindJSON 的参数类型是 interface{},在 Go 中,如果要通过一个函数修改参数的内容,必须传递指针,这样函数才能直接操作参数的内存地址,进而修改参数的值。否则,如果传递的是值类型,Go 语言会复制该值,导致函数内对该副本的修改不会影响到原始变量。 示例对比 以下是对比: 正确写法 parms := make(map[string]interface{}) err := context.BindJSON(&parms) // 传入指针 // 解析后 parms 中将包含 JSON 数据 错误写法 parms := make(map[string]interface{}) err := context.BindJSON(parms) // 传入值,解析无法修改 parms 内容 // 解析不会修改 parms 的内容 小结 总之,context.BindJSON 需要一个指针类型的参数,因此必须使用 &parms。 */ func (o OrderControllerStruct) CreateOrder(context *gin.Context) { //name := context.PostForm("name") //price := context.PostForm("price") // 使用map接受json 数据 parms := make(map[string]interface{}) //使用 Gin 的 BindJSON 方法解析 JSON 数据并将其绑定到 parms // 思考 这里 err := context.BindJSON(&parms) 可以写成 err := context.BindJSON(parms) 吗? // 不可以。 err := context.BindJSON(&parms) if err == nil { common.Succeed(1, context, parms, 10, "订单创建成功") } //common.Succeed(1, context, name, 10, price) }
简单示例 func (o OrderControllerStruct) CreateOrder(context *gin.Context) { name := context.PostForm("name") price := context.PostForm("price") common.Succeed(1, context, name, 10, price) }
curl --location 'http://127.0.0.1:9999/order/create' \ --form 'name="小蛋糕"' \ --form 'price="100.00"'
package controller import ( "github.com/gin-gonic/gin" "go-ranking/common" ) type UserControllerStruct struct { } func (u UserControllerStruct) GetUserInfo(c *gin.Context) { common.Failed(4004, c, "没有相关信息") } func (u UserControllerStruct) CreateUser(context *gin.Context) { common.Succeed(1, context, nil, 1, "创建成功") } func (u UserControllerStruct) DelUser(context *gin.Context) { common.Succeed(1, context, nil, 1, "删除成功") } func (u UserControllerStruct) UpdateUser(context *gin.Context) { common.Failed(0, context, "删除失败") }
package router import ( "github.com/gin-gonic/gin" "go-ranking/controller" ) // Router 路由,这里方法名要大写,因为要导出出去,在别的包里使用 func Router() *gin.Engine { r := gin.Default() userGroup := r.Group("/user") // 注意这里,他们不是一起的。 { userGroup.GET("/info", controller.UserControllerStruct{}.GetUserInfo) //userGroup.GET("/list", func(c *gin.Context) { // c.JSON(http.StatusOK, gin.H{"message": "list"}) //}) userGroup.GET("/create", controller.UserControllerStruct{}.CreateUser) //userGroup.DELETE("/delete", controller.DelUser) userGroup.GET("/delete", controller.UserControllerStruct{}.DelUser) userGroup.PUT("/put", controller.UserControllerStruct{}.UpdateUser) } return r }
package common import ( "github.com/gin-gonic/gin" "net/http" ) // json返回的数据结构 type JsonStruct struct { Code int `json:"code"` Data interface{} `json:"data"` Count int64 `json:"count"` Msg interface{} `json:"msg"` } type ErrorStruct struct { Code int `json:"code"` Msg interface{} `json:"msg"` } func Succeed(code int, c *gin.Context, data interface{}, count int64, msg interface{}) { json := &JsonStruct{code, data, count, msg} c.JSON(http.StatusOK, json) } func Failed(code int, c *gin.Context, msg interface{}) { json := &ErrorStruct{code, msg} c.JSON(http.StatusOK, json) } //func Demo(code int, c *gin.Context, msg interface{}) { // json := &JsonStruct{Code: code, Msg: msg} // c.JSON(http.StatusOK, json) //} /** 补充一个 关于 类型的值实例 和 类型的指针实例 区别、 &JsonStruct{} 和 JsonStruct{} 在 Go 中的区别在于它们的内存分配和类型。 1. JsonStruct{}:表示创建一个 JsonStruct 类型的值实例。 • 直接使用 JsonStruct{} 会在栈上分配一个 JsonStruct 类型的值,表示这个结构体的值本身。 • 当你使用 JsonStruct{} 时,得到的是一个结构体的副本。 2. &JsonStruct{}:表示创建一个 JsonStruct 类型的指针实例。 • 使用 &JsonStruct{} 会在堆上分配结构体值并返回一个指向该值的指针(类型为 *JsonStruct)。 • 返回的指针允许你直接修改结构体字段,而不会产生副本。 示例 假设有以下 JsonStruct 结构体: type JsonStruct struct { Code int Msg string } 然后我们来对比两种创建方式的不同: // 创建一个 JsonStruct 值实例 jsonValue := JsonStruct{Code: 200, Msg: "Success"} // 创建一个 JsonStruct 指针实例 jsonPointer := &JsonStruct{Code: 200, Msg: "Success"} 使用场景 • 值实例 (JsonStruct{}): • 适合在不需要对原始数据进行修改或传递副本时使用。 • 不适合处理大量数据,因为每次传递时都会复制结构体的数据。 • 指针实例 (&JsonStruct{}): • 适合在需要修改结构体字段,或在函数中传递以节省内存时使用。 • 对于较大结构体,指针实例更高效,因为不需要复制整个结构体。 示例:在函数中传递 func modifyValue(js JsonStruct) { js.Code = 500 // 只会修改副本 } func modifyPointer(js *JsonStruct) { js.Code = 500 // 修改指针所指向的原始数据 } func main() { jsValue := JsonStruct{Code: 200, Msg: "Original"} modifyValue(jsValue) fmt.Println(jsValue.Code) // 输出: 200 (未修改) jsPointer := &JsonStruct{Code: 200, Msg: "Original"} modifyPointer(jsPointer) fmt.Println(jsPointer.Code) // 输出: 500 (已修改) } 总结 • JsonStruct{}:创建结构体的值实例,每次使用时生成一个副本。 • &JsonStruct{}:创建结构体的指针实例,直接操作原始数据,更节省内存。 */
package common import ( "github.com/gin-gonic/gin" "net/http" ) // json返回的数据结构 type JsonStruct struct { Code int `json:"code"` Data interface{} `json:"data"` Count int64 `json:"count"` Msg interface{} `json:"msg"` } func Succeed(code int, c *gin.Context, data interface{}, count int64, msg interface{}) { json := &JsonStruct{code, data, count, msg} c.JSON(http.StatusOK, json) } func failed(code int, c *gin.Context, msg interface{}) { json := &JsonStruct{Code: code, Msg: msg} c.JSON(http.StatusOK, json) } /** 补充一个 关于 类型的值实例 和 类型的指针实例 区别、 &JsonStruct{} 和 JsonStruct{} 在 Go 中的区别在于它们的内存分配和类型。 1. JsonStruct{}:表示创建一个 JsonStruct 类型的值实例。 • 直接使用 JsonStruct{} 会在栈上分配一个 JsonStruct 类型的值,表示这个结构体的值本身。 • 当你使用 JsonStruct{} 时,得到的是一个结构体的副本。 2. &JsonStruct{}:表示创建一个 JsonStruct 类型的指针实例。 • 使用 &JsonStruct{} 会在堆上分配结构体值并返回一个指向该值的指针(类型为 *JsonStruct)。 • 返回的指针允许你直接修改结构体字段,而不会产生副本。 示例 假设有以下 JsonStruct 结构体: type JsonStruct struct { Code int Msg string } 然后我们来对比两种创建方式的不同: // 创建一个 JsonStruct 值实例 jsonValue := JsonStruct{Code: 200, Msg: "Success"} // 创建一个 JsonStruct 指针实例 jsonPointer := &JsonStruct{Code: 200, Msg: "Success"} 使用场景 • 值实例 (JsonStruct{}): • 适合在不需要对原始数据进行修改或传递副本时使用。 • 不适合处理大量数据,因为每次传递时都会复制结构体的数据。 • 指针实例 (&JsonStruct{}): • 适合在需要修改结构体字段,或在函数中传递以节省内存时使用。 • 对于较大结构体,指针实例更高效,因为不需要复制整个结构体。 示例:在函数中传递 func modifyValue(js JsonStruct) { js.Code = 500 // 只会修改副本 } func modifyPointer(js *JsonStruct) { js.Code = 500 // 修改指针所指向的原始数据 } func main() { jsValue := JsonStruct{Code: 200, Msg: "Original"} modifyValue(jsValue) fmt.Println(jsValue.Code) // 输出: 200 (未修改) jsPointer := &JsonStruct{Code: 200, Msg: "Original"} modifyPointer(jsPointer) fmt.Println(jsPointer.Code) // 输出: 500 (已修改) } 总结 • JsonStruct{}:创建结构体的值实例,每次使用时生成一个副本。 • &JsonStruct{}:创建结构体的指针实例,直接操作原始数据,更节省内存。 */
package main import ( "go-ranking/router" ) func main() { r := router.Router() r.Run(":9999") }
package router import ( "github.com/gin-gonic/gin" "net/http" ) // Router 路由,这里方法名要大写,因为要导出出去,在别的包里使用 func Router() *gin.Engine { r := gin.Default() userGroup := r.Group("/user") // 注意这里,他们不是一起的。 { userGroup.GET("/list", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "list"}) }) userGroup.POST("/create", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "create"}) }) userGroup.DELETE("/delete", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "delete"}) }) userGroup.PATCH("/patch", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "patch "}) }) userGroup.PUT("/put", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"message": "put "}) }) } return r }
对核心代码做一些解释
• 函数声明:
• func Router() 表示定义一个名为 Router 的函数。
• 返回值 *gin.Engine 表示这个函数返回一个指向 gin.Engine 实例的指针,gin.Engine 是 Gin 框架的核心路由器。
• *gin.Engine:
• gin.Engine 是 Gin 的核心类型,代表整个 HTTP 路由器和中间件系统。
• 通过 gin.Engine 实例,可以定义应用的路由、请求处理逻辑、中间件等。
还是比较有帮助的,点个赞
defer:延迟执行,先defer的后执行
panic 程序直接终止
recover在defer中执行,让程序恢复正常的状态去执行
3-4章节大纲课后复习大纲
如何获取get方式xxx?id=xxx&name=xxx中的id和name
如何获取post方式请求的body中的表单对象
如何获取post方式请求的body中的json对象(map和结构体两种方式获取)
66666666666666
产品经理不一定要有独立编码能力,但是适当了解一些技术原理,不至于提出“App的主题颜色根据手机外壳的颜色来自动调整”的这种需求了解一些常用的专业技术术语,可以更好的和程序员沟通协作,当程序员讨论构建某个功能时,咱们至少要能听懂他们在讲什么,问题出在哪里。
一、懂技术的产品经理有三大优势
1. 懂技术的产品经理,和开发的沟通更顺畅
听得懂技术专业术语,明白技术实现原理,在传递需求时更容易让技术同学理解,达成共识。
撰写PRD或需求评审时,知道技术关心什么,对技术细节的阐述更加到位全面。
线上有异常时,快速定位问题范围,找到相应的技术同事,加速问题的修复。
记录下来
还可以
不错
1