- goto
- 和C语言一样,可直接跳转到指定标签位置,往下执行
- 不同于break和continue,goto的标签可以指定为任何语句,包括空语句。
- break
- 不同于goto标签,break标签只能指定
for
、switch
和select
语句,并且break
语句只能跳转到当前语句的外部,如下面的代码时无法编译通过的:
FirstLoop: for i := 0; i < 10; i++ { } for i := 0; i < 10; i++ { break FirstLoop }
- 不同于goto标签,break标签只能指定
- continue
- 不同于break标签,continue标签只能修饰
foo
,因为另外两个没办法循环执行
- 不同于break标签,continue标签只能修饰
- error-pointer-prog
- 不同于C++,Go是不支持函数重载的,而指针类型函数和原类型函数如果定义一样,是算一个函数的,会编译报错
method redeclared
- 一个接口只关心函数行为,而不关心当前函数是指针函数,还是非指针函数。如果是指针函数,就有可能影响当前指针指向对象的内容。如果不是指针函数,无论这个函数里面做了什么行为,原类型一直保持不变。
- 不同于C++,Go是不支持函数重载的,而指针类型函数和原类型函数如果定义一样,是算一个函数的,会编译报错
- flag
- 定义flag名字、默认值、描述已经内容接收变量 ->
flag.Parse()
-> 操作接收呢容 - 命令行数据格式:
go run main.go -flagname=flagvalue
- 定义flag名字、默认值、描述已经内容接收变量 ->
- context-for-sync
- context可以用做主线程控制子线程的通信机制
- 有三种context:cancelContext, timeoutContext, deadlineContext
- cancelContext需要主动调用
cancel
,信号量才会Done
。另外两种除了主动调用cancel
外,时间到了,也会Done
。
- context-for-data
context.WithValue()
是向子线程传递数据的方法,由于没有cacel
机制,无法控制子线程的运作。
- gin-input-from-url-query-string
- 在浏览器中输入:
localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15
- URL可以带query string的
- 对GET请求,gin只启用
Form
组件解析query string
c *gin.Context
是回调函数接收的参数c.ShouldBind(&person)
接受gin.Context
赋值type Persion struct
定义了Form
结构,定义key-value,和顺序无关
- gin-input-from-path
- 在浏览器中输入:
localhost:8085/testing/1
- 服务器端得到id的值为1
- gin-output-with-json
- 在浏览器中输入以下测试数据:
1.http://localhost:8080/getb?field_a=hello&field_b=world 输出:{"a":{"FieldA":"hello"},"b":"world"} 2.http://localhost:8080/getc?field_a=hello&field_c=world 输出:{"a":{"FieldA":"hello"},"c":"world"} 3.http://localhost:8080/getd?field_x=hello&field_d=world 输出:{"d":"world","x":{"FieldX":"hello"}}
- 第一层的key,代码指定
- 第二层的key,代码根据结构体定义推到出来
- 嵌入的JSON格式,由三种指定方法:1.内部用结构体;1.内部用结构体指针;2.内部定义结构体;
- gin-middleware-process
- 新建一个空的gin引擎,
gin.Default()
默认回加上gin自己的Logger - 添加中间件
Logger()
,中间件是一个HandlerFunc
- 在中间件中,
c.Next()
之前都是Router回调函数之前执行的,之后都是Router回调函数之后执行的 c.Set()
可以往context中填入[key, value]
- 新建一个空的gin引擎,
- gin-static-router-server
- 有三种方法可以配置静态服务路由
- gin-router-group
- 在
Group
上绑定的中间件和根上绑定中间件的区别Group
上绑定的中间件只会在当前Group
下已经指定的URL下才会执行Group
根上的中间件,不需要指定URL方法,就会执行- 例如,例子中必须在
localhost:8080/api/test
下才会触发中间件,而如果中间件直接在router
根下绑定,任何以/api
为前缀的URL都会触发此中间件。
- 在
- logger-logrus-hello
- 基本输出格式为(field:[animal, walrus]):
time="2020-01-16T19:16:03+08:00" level=info msg="A walrus appears" animal=walrus
- logger-logrus-json
- 可设置输出格式和输出级别
- logger-location-file
- 可创建多个logger
- 可方便输出到文件
logrus
提供了日志钩子,可以挂上数据库日志操作的钩子函数,这样logrus
不仅本地能将log打到本地的窗口,也会打印到数据库中。- logrus-log-mongodb
logrus
的Hook
接口
type Hook interface { Levels() []Level Fire(*Entry) error }
github.com/LyricTian/logrus-mongo-hook/hook.go
实现了这个接口- 因此,
mongohook.DefaultWithURL()
可以产生一个Hook
,挂到logrus
上,即可将日志写到数据库中了 - 启动程序后,mongodb会多出一条文档信息:
{ "_id" : ObjectId("5e20957ab48172e81ec607a7"), "foo-warn" : "bar-wan", "level" : 3, "message" : "test warn", "created" : NumberLong(1579193722) }
- gorm-sqlite-hello
- 安装好sqlite即可运行此程序
- gorm只支持关系型数据库
- 连接数据库 -> 绑定数据模型 -> 操作数据库(CQUD)
- JWT全称JSON Web Token,是一种加密算法,常用于互联网请求的验证
- 实现机制
- JWT-Token和Session的区别和联系
- 参考link
- Session验证的
SessionID
是存在服务器端的,而Token验证由Token自己就可以完成,不需要再利用服务器端的存储信息进行验证。 - 不需要在服务器端存储验证信息,是JWT-Token方式的最大优势。
- 示例代码go-jwt-server,参考link
- 启动服务
- 用
Postman
模拟客户端行为- 发送Get请求到:http://localhost:8000/welcome
收到
401 Unauthorized
错误,因为我们没有登录验证- 发送Post请求到:http://localhost:8000/login , 并带上JSON格式的用户户名密码
{"username":"user1","password":"password1"}
。服务器写死了两个用户:user1和user2。
验证成功,服务器发送了Cookie给客户端
- 打开Postman的
Cookies
串口,发现localhost
下多了一个token
Cookie,这就是服务器发送过来的,里面由token信息 - 再次发送Get请求到:http://localhost:8000/welcome
收到
Welcome user1!
回复- 删除Postman的
token
Cookie - 再次发送Get请求到:http://localhost:8000/welcome
收到
401 Unauthorized
错误,因为客户端的Cookie被删除了,需要再次登录验证 - login HTTP POST包,申请验证用户
- client -> server
POST /login HTTP/1.1\r\n Content-Type: application/json\r\n User-Agent: PostmanRuntime/7.21.0\r\n Accept: */*\r\n Cache-Control: no-cache\r\n Host: 192.168.50.64:8000\r\n Accept-Encoding: gzip, deflate\r\n Content-Length: 43\r\n Connection: keep-alive\r\n {"username":"user1","password":"password1"}
- server -> client
HTTP/1.1 200 OK\r\n Response Version: HTTP/1.1 Status Code: 200 Response Phrase: OK Set-Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXIxIiwiZXhwIjoxNTc5MjUzODI2fQ.NYYRmICsUPURGXndOt7gjGvX-x3tvZsjTEyjR453g_M; Expires=Fri, 17 Jan 2020 09:37:06 GMT\r\n
- 收到验证Cookie后,客户端向服务器发送welcome HTTP GET包
- client -> server
GET /welcome HTTP/1.1\r\n User-Agent: PostmanRuntime/7.21.0\r\n Accept: */*\r\n Cache-Control: no-cache\r\n Postman-Token: b1f6a751-7041-4abf-895b-c0efb56b1dd9\r\n Host: 192.168.50.64:8000\r\n Accept-Encoding: gzip, deflate\r\n Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXIxIiwiZXhwIjoxNTc5MjUzODI2fQ.NYYRmICsUPURGXndOt7gjGvX-x3tvZsjTEyjR453g_M\r\n Connection: keep-alive\r\n
- server -> client
HTTP/1.1 200 OK\r\n Content-Length: 14\r\n Content-Type: text/plain; charset=utf-8\r\n Welcome user1!
OAuth是一种授权机制。数据的所有者告诉系统,同意授权第三方应用进入系统,获取这些数据。系统从而产生一个短期的进入令牌(token),用来代替密码,供第三方应用使用。参见Link
个人理解,JWT是一种Token生成和校验的方法,OAuth2也用到了技术。OAuth2利用JWT技术,实现了第三方授权机制的验证模式。OAuth2的实现机制有多种选择。
- 上面JWT实现的保护和OAuth2的相同点 都生成Token给客户端。目的是,提供一个短期令牌(而不是密码)给客户端,让客户端可以在短期内利用这个令牌,不再用用户授权,就可以访问受保护的资源。
- 上面JWT实现的保护和OAuth2的不同的地方 JWT方法需要客户端提供用户名和密码(或者说登录后),才提供Token,相当于把用户和客户端看成一个人了。而OAuth将客户端和用户分离,客户端和用户之间通过权限服务器沟通,用户授权权限服务器可以给此客户端Token,服务器就生成Token给此客户端用。这样做的好处是,用户的用户名和密码只有用户知道,权限服务器和客户端都不知道。
- go-oauth-simple-demo
- 此代码是简化的示例,用户授权的过程体现在事先定死的
CLIENT_ID
和CLIENT_SECRET
。只要权限服务收到此客户端的Token申请需求,就生成Token给客户端。 - 执行流程:
- 开启服务
go run main
- 浏览器申请Token
localhost:9094/token?grant_type=client_credentials&client_id=000000&client_secret=999999&scope=all
- 浏览器收到回复,例如
{"access_token":"T2LMWRAWO_M3MBLG17S-UQ","expires_in":7200,"scope":"all","token_type":"Bearer"}
- 利用上面的Token,操作资源
http://localhost:9094/protected?access_token=T2LMWRAWO_M3MBLG17S-UQ
- 收到回复
Hello, I'm protected
- go-dependency-inject-dig,参考link
- Dig的适用范围:当某对象的依赖较多且逻辑复杂时,可将依赖的构造和初始化的工作交给Dig,通过依赖注入的方式,构造此对象。
- Dig实现流程:
- 注册依赖构造函数:将所有依赖的构造函数注册给Dig(通过Provide),单个依赖的构造可以依赖其他依赖。注意,这一步并不会执行依赖的构造(真正的构造是在第一次调用
invoke
的时候),而且Provide调用的顺序也不影响后面的依赖构造顺序。 - 完成依赖注册:Dig返回一个
Container
,包含所有依赖的信息。此时,所有的依赖依旧还没有构造。 - 调用依赖函数:当第一次调用
Container.Inoke(...)
时,构造所有依赖。Inoke
函数用于调用所有依赖的方法。
- 注册依赖构造函数:将所有依赖的构造函数注册给Dig(通过Provide),单个依赖的构造可以依赖其他依赖。注意,这一步并不会执行依赖的构造(真正的构造是在第一次调用
- go-restful-api-documentation
- API文档生成流程:
- 根据文档在代码中加入
swaggo
可以解析的注释 - 利用
swag init
命令解析代码中的注释,生成三个文件
swag init -g ./swagger.go -o ./docs - docs.go - swagger.json - swagger.yaml
- 在
doc
文件夹下的swagger静态文件可以解析上面的文件,生成对应的API文件静态页面doc
文件夹下的swagger静态文件不需要修改,可以直接移植到其他项目
- 在项目代码中添加静态文件服务,服务于
doc
目录,访问如下网址即可查看文档
- 根据文档在代码中加入