我们推荐使用go.mod
来管理项目依赖,这也是Golang官方推荐的包管理方式。
https://github.com/gogf/gf-demos/blob/master/go.mod
module github.com/gogf/gf-demos
require github.com/gogf/gf latest
go 1.12
其中注意module
名称设置为github.com/gogf/gf-demos
。这里我们只需要依赖GF
框架即可。其中的go 1.12
表示运行该项目所需的最低Go
版本,这里也可以不设置。Goland
会自动帮我们设置为当前使用的Go
版本。
GF
框架的核心组件均实现了便捷的文件配置管理方式,包括Server
、日志组件、数据库ORM
、模板引擎等等,非常强大便捷。具体的配置项可以查看后续对应的章节介绍。GF
框架大部分的核心组件配置项是不区分大小写的,但是为保证规范统一,我们建议在配置文件中统一使用小驼峰形式。
https://github.com/gogf/gf-demos/blob/master/config/config.example.toml
需要注意哦,仓库中提供的config.example.toml
为配置文件示例,如果想要正常运行示例程序,你可以将其拷贝或者重命名为config.toml
。
配置文件中如果存在文件路径相关的配置,建议统一使用绝对路径配置,会少踩很多坑。如果使用了相对路径,你得清楚知道该配置的路径相对的是哪个路径(不同的部署方式,工作目录可能不可预知)。
# HTTP Server.
[server]
address = ":8199"
serverRoot = "public"
serverAgent = "gf-demos"
logPath = "/tmp/log/gf-demos/server"
nameToUriType = 2
routeOverWrite = true
# Logger configurations.
[logger]
path = "/tmp/log/gf-demos"
level = "all"
stdout = true
# Template view configurations.
[viewer]
path = "template"
defaultFile = "index.html"
# Database configurations.
[database]
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
debug = true
# 数据库日志对象配置
[database.logger]
Path = "/tmp/log/gf-demos/sql"
Level = "all"
Stdout = true
# GF-CLI工具配置
[gfcli]
# 自定义DAO生成配置(默认是读取database配置)
[[gfcli.gen.dao]]
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
tables = "user"
在boot
包中执行代码层级的初始化,比如一些组件模块的设置。
https://github.com/gogf/gf-demos/blob/master/boot/boot.go
package boot
import (
_ "github.com/gogf/gf-demos/packed"
"github.com/gogf/gf/frame/g"
"github.com/gogf/swagger"
)
// 用于应用初始化。
func init() {
s := g.Server()
s.Plugin(&swagger.Swagger{})
}
可以看到,我们的包初始化管理使用了包初始化方法init
来做隐式初始化,这样做的好处是可以在boot
目录中使用不同的go
文件注册不同的init
来分别实现不同的初始化配置管理,在业务比较复杂的项目中比较实用。
这里使用了分组路由的注册方式,分组路由也是推荐的路由注册方式。由于gf-demos
项目包含其他的示例功能,因此该路由中包含了其他的一些路由注册项,仅供参考。
https://github.com/gogf/gf-demos/blob/master/router/router.go
package router
import (
"github.com/gogf/gf-demos/app/api"
"github.com/gogf/gf-demos/app/service"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func init() {
s := g.Server()
// 分组路由注册方式
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(
service.Middleware.Ctx,
service.Middleware.CORS,
)
group.ALL("/chat", api.Chat)
group.ALL("/user", api.User)
group.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(service.Middleware.Auth)
group.ALL("/user/profile", api.User.Profile)
})
})
}
可以看到,我们的路由注册管理也使用了包初始化方法init
实现隐式初始化,这样做的好处是可以在router
目录中使用不同的go
文件注册不同的init
来分别实现不同的路由注册。当项目的路由比较多的时候,可以采用不同的go
文件管理不同的路由,这在团队协作的项目中也比较方便。
这里通过s.Group
方法使用了分组路由实现路由注册,并通过group.All
来实现具体的路由注册,分组路由请参考后续 路由注册-分组路由 章节。
如果您对HTTP Method
没有特殊的需求,建议统一使用ALL
方法注册路由,使得该路由可以被任意的HTTP Method
访问。特别是在跨域场景中,可能会同一个路由接口需要同时满足POST
和OPTIONS
请求,这种情况只有使用ALL
方法注册路由。
这里使用了group.Middleware
方法注册中间件,可以将中间件看做请求的拦截器,可以在中间件中实现特定的请求处理后转发给下一层请求继续执行。中间件可以注册多个,WebServer
会按照中间件注册的顺序执行。中间件的具体介绍请参考后续 路由管理-中间件/拦截器 章节。
在该路由的配置中,所有接口均绑定了两个中间件:
service.Middleware.Ctx
初始化上下文变量service.Middleware.CORS
允许跨域请求的中间件其中/user/profile
路由绑定了鉴权中间件service.Middleware.Auth
,需要鉴权控制才能访问。
由于boot
和router
包使用了init
包初始化方式来进行相关配置,因此我们需要使用:
import _ "PATH"
方式来引入。
需要注意,由于Go
的import
存在先后顺序,往往需要将这两个包置于main.go
所有业务包的最上方引入。
main
包每个项目至少存在一个package main
,用于程序的入口执行。
/main.go
package main
import (
_ "github.com/gogf/gf-demos/boot"
_ "github.com/gogf/gf-demos/router"
"github.com/gogf/gf/frame/g"
)
func main() {
g.Server().Run()
}
需要非常注意:boot
包的引入往往需要在main
包中的最顶层位置,以保证应用的初始化最开始进行。特别是一些单例对象的初始化/配置管理操作往往放在boot
包中,这样其他包才能正常使用到这些单例对象。
这里建议引入boot
包、router
包和其他包之间加入一个空行以作区分,特别是Goland
IDE的import
插件不会将引入包进行自动排序。
我们可以使用IDE
执行运行,也可以使用以下命令编译运行。
$ go build main.go
$ ./main
执行后,终端输出的路由表如下:
SERVER | DOMAIN | ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /chat | github.com/gogf/gf-demos/app/api.(*apiChat).Index | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /chat/index | github.com/gogf/gf-demos/app/api.(*apiChat).Index | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /chat/setname | github.com/gogf/gf-demos/app/api.(*apiChat).SetName | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /chat/websocket | github.com/gogf/gf-demos/app/api.(*apiChat).WebSocket | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /swagger/* | github.com/gogf/swagger.(*Swagger).Install.func1.1 | HOOK_BEFORE_SERVE
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /user/checknickname | github.com/gogf/gf-demos/app/api.(*apiUser).CheckNickName | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /user/checkpassport | github.com/gogf/gf-demos/app/api.(*apiUser).CheckPassport | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /user/issignedin | github.com/gogf/gf-demos/app/api.(*apiUser).IsSignedIn | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /user/profile | github.com/gogf/gf-demos/app/api.(*apiUser).Profile-fm | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm,service.(*serviceMiddleware).Auth-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /user/signin | github.com/gogf/gf-demos/app/api.(*apiUser).SignIn | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /user/signout | github.com/gogf/gf-demos/app/api.(*apiUser).SignOut | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
default | default | :8199 | ALL | /user/signup | github.com/gogf/gf-demos/app/api.(*apiUser).SignUp | service.(*serviceMiddleware).Ctx-fm,service.(*serviceMiddleware).CORS-fm
----------|---------|---------|--------|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------------------------
我们通过curl
命令来对其中两个接口执行简单的测试。
/user/signup
注册一个账号test001
,昵称为john
,密码为123456
。
curl -d 'nickname=john&passport=test001&password=123456&password2=123456' http://127.0.0.1:8199/user/signup
{"data":null,"code":0,"message":"ok"}
我们再次使用刚才的信息注册一次试试。
curl -d 'nickname=john&passport=test001&password=123456&password2=123456' http://127.0.0.1:8199/user/signup
{"data":null,"code":1,"message":"账号 test001 已经存在"}
可以看到注册失败了,相同名称只能注册一个账号。
/user/signin
我们先访问获取用户信息的接口,验证鉴权中间件是否生效。
curl http://127.0.0.1:8199/user/profile
Forbidden
我们用刚才注册的账号登录。
curl -i -d 'passport=test001&password=123456' http://127.0.0.1:8199/user/signin
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Origin,Content-Type,Accept,User-Agent,Cookie,Authorization,X-Auth-Token,X-Requested-With
Access-Control-Allow-Methods: GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE
Access-Control-Allow-Origin: *
Access-Control-Max-Age: 3628800
Content-Type: application/json
Server: gf-demos
Set-Cookie: gfsessionid=BZT1SP2OX980EHALYV; Path=/; Expires=Sun, 10 Jan 2021 14:56:36 GMT
Date: Sat, 11 Jan 2020 14:56:36 GMT
Content-Length: 37
{"code":0,"message":"ok","data":null}
我们这里使用了-i
选项用于终端打印出服务端返回的Header
信息,目的是为了获取sessionid
。GF
框架默认的sessionid
名称为gfsessionid
,我们看到返回的Header
中已经有了,并且是通过Cookie
方式返回的。
随后我们再次访问获取用户信息接口,并且这次提交gfsessionid
,该信息可以通过Header
也可以通过Cookie
提交,服务端都是能够自动识别的。
curl -H 'gfsessionid:BZT1SP2OX980EHALYV' http://127.0.0.1:8199/user/profile
{"code":0,"message":"","data":{"id":1,"passport":"test001","password":"123456","nickname":
接下来可以看一下代码质量中关于如何测试以及项目部署中如何部署,一般来说单个应用的话直接独立部署即可,如果存在多个应用则可以代理部署的方式通过nginx代理,如果需要自动化运维的话那么推荐docker部署,这个也是官方推荐的方式:https://goframe.org/pages/viewpage.action?pageId=1114332,当然,关于GF框架的工具、组件和设计实现都是值得学习的地方,感兴趣的也可以继续深入。
接下来Go相关的内容我可能会一边总结docker和k8s,一边总结前端的内容(感觉学了Go不搞点web应用好像少了点什么),之后会将我的个人网站做下升级开发之后放到阿里云上(之前一直放在GitHub Page上),然后通过自动化运维的方式进行维护,以此来实践所学(目前阶段项目也不是特别忙,可以有很一些时间来学习和总结)。