@SovietPower
2022-06-24T15:34:20.000000Z
字数 7172
阅读 1140
学习笔记
作业部落链接:https://www.zybuluo.com/SovietPower/note/1829392
参考:
https://github.com/skyhee/gin-doc-cn
https://github.com/gin-gonic/gin
https://go-zh.org/doc/articles/wiki/
https://gin-gonic.com/zh-cn/docs/examples/ (很简略的官方文档)
gorm:
文档:https://gorm.cn/zh_CN/docs/sql_builder.html
https://www.bilibili.com/video/BV1E64y1472a
目前:
由于官方golang.org
被墙了,所以不能直接下载(只开科学上网不全局代理也不行)。
通过如下设置,使用goproxy.io
下载:
按照 https://blog.csdn.net/qq_34284638/article/details/104944319 或 https://goproxy.io/zh/ 进行设置:
# 启用 Go Modules 功能
go env -w GO111MODULE=on
# 配置 GOPROXY 环境变量
go env -w GOPROXY=https://goproxy.io,direct
# (可选)设置不走 proxy 的私有仓库或组,多个用逗号相隔
go env -w GOPRIVATE="git.mycompany.com,github.com/my/private"
然后可以go get -u github.com/gin-gonic/gin
安装gin。
注意不是安装在GOROOT
指向的go\src
里面,而是在GOPATH
指向的go\pkg\mod
(GOPATH
默认为C:\Users\UserName\go
)。
查找导入包时,会在GOROOT
和GOPATH
下的src
中查找。也可以修改go env -w GOPATH=[安装目录]\go
或者随便一个目录?
初始设置:
set GO111MODULE=C:\Users\UserName\go
...
set GOPATH=[]
set GOPRIVATE=
set GOPROXY=https://proxy.golang.org,direct
set GOROOT=[go的安装目录]
使用go env -u attr_name
初始化一个属性。
使用gin
导入"github.com/gin-gonic/gin"
包。
注意因为使用了go mod
,导致包不被下载到$GOPATH\src\
,而是下载到了$GOPATH\src\pkg\
?所以需要用go.mod
导入包(go.mod
用来管理模组)。
在项目根目录执行两条命令:
go mod init ModuleName
go mod edit -require github.com/gin-gonic/gin@latest // 或直接用go mod tidy
ModuleName为当前模块名。
可能还需要go mod tidy
更新go.mod
。
vscode go插件安装失败
进行完这些设置后,再按提示安装插件即可(会装在$GOPATH\bin\
下)。
fresh
https://blog.csdn.net/qq_34284638/article/details/104944319
listen tcp: address 8000: missing port in address
Run
的参数端口前不能少冒号(如:8080
不是8080
)。
context
用context获取路由参数:
https://www.cnblogs.com/peteremperor/p/14033453.html
URL 参数通过 DefaultQuery 或 Query 方法获取的例子:
// url 为 http://localhost:8080/welcome?name=ningskyer时
// 输出 Hello ningskyer
// url 为 http://localhost:8080/welcome时
// 输出 Hello Guest
router.GET("/welcome", func(c *gin.Context) {
name := c.DefaultQuery("name", "Guest") //可这样设置默认值
// 是 c.Request.URL.Query().Get("lastname") 的简写
lastname := c.Query("lastname")
fmt.Println("Hello %s", name)
})
gin.H
gin.H 可以简化生成 json 的方式,如果需要嵌套 json,嵌套 gin.H 就可以了。
gin.HandlerFunc
https://pkg.go.dev/github.com/gin-gonic/gin#section-readme
HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)
所有的接口都要由路由来进行管理。
Gin的路由支持GET,POST,PUT,DELETE,PATCH,HEAD,OPTIONS等请求。此外还有Any请求,同时支持以上的所有请求。
创建无中间件的路由router:router := gin.New()
。
创建引入默认中间件的路由router:router := gin.Default()
。
Default()
与New()
的区别是,Default()
在New
一个engine后,通过Use()
传入了Logger
和Recovery
两个中间件。
Logger
对日志进行记录;Recovery
当有painc时, 进行500的错误处理。
定义加密存储空间?store := cookie.NewStore([]byte("salt_string"))
使用session
中间件:router.Use(sessions.Sessions("mySession", store))
使用session:session := sessions.Default(c)
(并不是初始化一个?)
将session信息保存:session.Save()
(因为定义存储时用了cookie所以存在cookie里)
设置session的例子:
https://github.com/gin-contrib/sessions#backend-examples
https://www.cnblogs.com/wind-zhou/p/13114548.html
直接获取Cookie
对应信息并修改也可以(Cookie(key), SetCookie(key, value, ...)
)。且最好这样?
func getCookieSession(c *gin.Context) {
cookie, err := c.Cookie("session_id")
if err != nil {
cookie = "NotSet"
c.SetCookie("session_id", "12345", 10, "/", "localhost", false, true)
}
fmt.Printf("Cookie session_id value: %s\n", cookie)
}
func main() {
router := gin.Default()
router.GET("/session", getCookieSession)
router.Run()
}
setCookie()
原型:SetCookie func(name string, value string, maxAge int, path string, domain string, secure bool, httpOnly bool)
例:context.SetCookie("user_cookie", string(u.Id), 123, "/", "localhost", false, true)
注:例子中必须使用
"localhost"
作为域名,不能用127.0.0.1
。
和代码无关,我访问地址有问题,我访问的是 127.0.0.1:8001,应该是localhost:8000 127.0.0.1通常是分配给“环回”或本地接口的IP地址。这是一个只能在同一主机内通信的“假”网络适配器。当您希望具有网络功能的应用程序仅为同一主机上的客户机提供服务时,通常会使用这种方法。在127.0.0.1上监听连接的进程将只接收该套接字上的本地连接。
“localhost”通常是127.0.0.1 IP地址的主机名。它通常在/etc/hosts中设置(或者在%WINDIR%下的等效窗口名为“hosts”)。您可以像使用任何其他主机名一样使用它—尝试“ping localhost”,看看它是如何解析为127.0.0.1的。
0.0.0.0有几个不同的含义,但是在本文中,当服务器被告知监听0.0.0.0时,这意味着“监听每个可用的网络接口”。从服务器进程的角度来看,IP地址为127.0.0.1的环回适配器与机器上的任何其他网络适配器一样,因此被告知监听0.0.0.0的服务器也将接受该接口上的连接。参数:
第一个参数name 为 cookie 名。
第二个参数value 为 cookie 值。
第三个参数maxAge 为 cookie 有效时长,当 cookie 存在的时间超过设定时间时,cookie 就会失效,它就不再是有效的 cookie,单位为秒。
第四个参数path 为 cookie 所在的目录。
第五个domain 为所在域,表示 cookie 作用范围,里面可以是localhost也可以是你的域名,看情况。
第六个secure 表示是否只能通过 https 访问,为true只能是https。
第七个httpOnly 表示 cookie 是否可以通过 js代码进行操作,为true时不能被js获取(比如jQuery获取不到)。
生成唯一UUID 作为session ID
几个包:
https://pkg.go.dev/github.com/google/uuid?utm_source=gopls(推荐)
https://github.com/gofrs/uuid(常用)
https://github.com/satori/go.uuid(已不使用)
https://github.com/pborman/uuid
gofrs的基本操作:
uuid.NewV4()
:生成V4版本(基于随机数)的随机uuid(返回UUID
即[Size]byte
和error
)(UUID
通过u.String()
可转为string)。
uuid.Must(uuid.NewV4())
:生成V4版本(基于随机数)的随机uuid,并检查错误。
uuid.From...()
:利用给定参数生成uuid。
使用后缀.wav
而不是.png
,可生成验证码语言,但过于难以听懂。可参考:
https://github.com/keep94/toolbox/blob/master/session_util/session.go (但不是用gin)
加密种类:https://blog.csdn.net/ctrip_tech/article/details/80125741
几种加密的简单示例:https://blog.csdn.net/weixin_42117918/article/details/82870809
bcrypt:https://www.cnblogs.com/niuben/p/13224221.html
https://blog.csdn.net/weixin_36532747/article/details/107064115
https://blog.csdn.net/weixin_42117918/article/details/111562045
http://dljz.nicethemes.cn/news/show-391481.html
在后端接收前端发送的数据时,使用json标签对应数据的bind赋值。
在后端发给前端数据时,使用json标签可更改发送的json数据的名称。不使用标签,则为原变量命名。
required
https://blog.csdn.net/weixin_42279809/article/details/107800081
binding:"required"
后则不能传入相应类型的零值,否则也会检验失败。如字符串不能传入空串,int不能传入0,bool不能传入false。
但用指针即可传入对应数据类型的零值。不传时为nil
。
例:
type Student struct {
Name string `json:"name" binding:"required"`
Age int `json:"age" binding:"required"`
Sex int `json:"sex" binding:"required"`
}
// 允许sex为0,可换为
type Student struct {
Name string `json:"name" binding:"required"`
Age int `json:"age" binding:"required"`
Sex *int `json:"sex" binding:"required"`
}
查询使用Raw
,更新使用Exec
!
用户上传头像
前端(检查文件及格式是否合法后)发送给后端文件名,后端取出该文件的扩展名,在合适目录下为其分配随机ID+扩展名,作为文件名。目录+文件名即为objectKey
。
client, err := oss.New(os.Getenv("OSS_END_POINT"), os.Getenv("OSS_ACCESS_KEY_ID"), os.Getenv("OSS_ACCESS_KEY_SECRET"))
if err != nil {
...
}
// 获取存储空间。
bucket, err := client.Bucket(os.Getenv("OSS_BUCKET"))
if err != nil {
...
}
// 获取扩展名
ext := filepath.Ext(service.Filename)
// 带可选参数的签名直传
options := []oss.Option{
oss.ContentType(mime.TypeByExtension(ext)),
}
key := "upload/avatar/" + uuid.Must(uuid.NewRandom()).String() + ext
// 上传图片
signedPutURL, err := bucket.SignURL(key, oss.HTTPPut, 600, options...)
if err != nil {
...
}
// 查看图片
signedGetURL, err := bucket.SignURL(key, oss.HTTPGet, 600)
if err != nil {
...
}
return serializer.Response{
Status: code,
Msg: e.GetMsg(code),
Data: map[string]string{
"key": key,
"put": signedPutURL,
"get": signedGetURL,
},
}
使用该objectKey
,签名一个get一个put的URL,返回给前端。前端使用put的URL和上传的数据,直接向OSS发起更新请求;使用get的URL得到图片。
const oReq = new XMLHttpRequest()
oReq.open('PUT', res.data.put, true)
oReq.send(option.file)
oReq.onload = () => {
this.imageURL = res.data.get
this.userForm.avatar = res.data.key
}
objectKey
会作为用户表的AvatarKey
属性保存在数据库。查看时,返回给前端一个签名的get URL。
func (user *User) AvatarURL() string {
client, _ := oss.New(os.Getenv("OSS_END_POINT"), os.Getenv("OSS_ACCESS_KEY_ID"), os.Getenv("OSS_ACCESS_KEY_SECRET"))
bucket, _ := client.Bucket(os.Getenv("OSS_BUCKET"))
signedGetURL, _ := bucket.SignURL(user.AvatarKey, oss.HTTPGet, 24*60*60)
//if strings.Contains(signedGetURL, "http://ailiaili-img-av.oss-cn-hangzhou.aliyuncs.com/?Exp") { // 未找到
//signedGetURL := "https://ailiaili-img-av.oss-cn-hangzhou.aliyuncs.com/img/noface.png"
//}
return signedGetURL
}
可选:使用原objectKey
,删除原图片。