# Web 项目
# 安装gone辅助工具
go install github.com/gone-io/gone/tools/gone@latest
安装可以使用gone命令:
gone -h
➜ demo gone -h NAME: gone - A new cli application USAGE: gone [global options] command [command options] [arguments...] DESCRIPTION: generate gone code or generate gone app COMMANDS: priest -s ${scanPackageDir} -p ${pkgName} -f ${funcName} -o ${outputFilePath} [-w] mock -f ${fromGoFile} -o ${outGoFile} create [-t ${template} [-m ${modName}]] ${appName} help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --help, -h show help (default: false)
支持的功能:
- create,创建一个gone app,暂时只支持创建web app
- priest,为项目自动生成 Priest 函数,了解更多
- 生成用于测试的mock代码
# 创建一个web项目并运行代码
# 创将一个名为 web-app 的项目
gone create web-app
cd web-app
make run
# 项目结构
➜ xxx tree
.
├── Dockerfile
├── Makefile
├── README.md
├── cmd
│ └── server
│ └── main.go
├── config
│ ├── default.properties
│ ├── dev.properties
│ ├── local.properties
│ └── prod.properties
├── docker-compose.yaml
├── go.mod
├── internal
│ ├── controller
│ │ └── demo_ctr.go
│ ├── interface
│ │ ├── domain
│ │ │ ├── demo.go
│ │ │ └── user.go
│ │ └── service
│ │ └── i_demo.go
│ ├── master.go
│ ├── middleware
│ │ ├── authorize.go
│ │ └── pub.go
│ ├── module
│ │ └── demo
│ │ ├── demo_svc.go
│ │ └── error.go
│ ├── pkg
│ │ └── utils
│ │ └── error.go
│ └── router
│ ├── auth_router.go
│ └── pub_router.go
└── tests
└── api
└── demo.http
17 directories, 23 files
- cmd/server/main.go: 启动文件,main函数所在文件
- config/: 项目配置文件目录,支持
.properties
文件 - internal/router/: 在该目录定义路由器
- internal/middleware/: 中间件目录,如果需要定义web中间件,在该目录编写
- internal/controller/: controller目录,在该目录中的文件定义路由
- internal/interface/service/: 该目录放服务的接口定义
- internal/domain/: 该目录放领域对象
- internal/entity/: 该目录放一些无逻辑的结构体,类似于Java 的POJO
- internal/module/: 模块目录,下面的每一个子目录实现一个模块的功能,一般是internal/interface/service/中定义的服务的业务实现;
- internal/pkg/: 在该目录可以放一些项目共用的工具代码
- internal/master.go: 存放MasterPriest函数
- internal/priest.go: gone priest 命令生成的 Priest函数,用于注册所有Goner
# Router
在目录internal/router
中分别实现了两个gin.IRouter
:
- pubRouter,公开的路由,挂载在该路由下的接口,请求将无需授权即可访问。
- authRouter,鉴权的路由,挂载在该路由下的接口,请求必须先要经过授权。
我们来分析internal/router/pub_router.go
的代码:
package router
import (
"web-app/internal/middleware"
"github.com/gone-io/gone"
"github.com/gone-io/gone/goner/gin"
)
const IdRouterPub = "router-pub"
//go:gone
func NewPubRouter() (gone.Goner, gone.GonerId) {
return &pubRouter{}, IdRouterPub
}
type pubRouter struct {
gone.Flag
gin.IRouter
root gin.IRouter `gone:"gone-gin-router"`
pub *middleware.PubMiddleware `gone:"*"`
}
func (r *pubRouter) AfterRevive() gone.AfterReviveError {
r.IRouter = r.root.Group("/api", r.pub.Next)
return nil
}
- 对于router,需要实现了
gin.IRouter
接口中定义的方法; - 结构体
pubRouter
内嵌了一个gin.IRouter
,就等于直接实现了gin.IRouter
接口,只在AfterRevive()
中将其赋予一个值就可以了; r.IRouter = r.root.Group("/api", r.pub.Next)
意思是当前路由是根路由下/api
的子路由,并且默认的增加了一个中间r.pub.Next
;root gin.IRouter
是一个Gone框架中github.com/gone-io/gone/goner/gin
包提供的一个框架级Goner,具名注入的gone-gin-router
。
# Controller
下面是Controller接口的定义:
// Controller 控制器接口,由业务代码编码实现,用于挂载和处理路由
// 使用方式参考 [示例代码](https://gitlab.openviewtech.com/gone/gone-example/-/tree/master/gone-app)
type Controller interface {
// Mount 路由挂载接口,改接口会在服务启动前被调用,该函数的实现通常情况应该返回`nil`
Mount() MountError
}
编写http接口,我们需要实现Controller
接口,在Mount
方法中实现接口路由的挂载,如internal/controller/demo_ctr.go
的代码:
package controller
import (
"web-app/internal/interface/service"
"web-app/internal/pkg/utils"
"github.com/gone-io/gone"
"github.com/gone-io/gone/goner/gin"
)
//go:gone
func NewDemoController() gone.Goner {
return &demoController{}
}
type demoController struct {
gone.Flag
demoSvc service.IDemo `gone:"*"` //注入依赖的服务
authRouter gin.IRouter `gone:"router-auth"` //注入路由器
pubRouter gin.IRouter `gone:"router-pub"` //注入路由器
}
func (ctr *demoController) Mount() gin.MountError {
//需要鉴权的路由分组
ctr.
authRouter.
Group("/demo").
GET("/show", ctr.showDemo)
//不需要鉴权的路由分组
ctr.
pubRouter.
Group("/demo2").
GET("/show", ctr.showDemo).
GET("/error", ctr.error).
GET("/echo", ctr.echo)
return nil
}
func (ctr *demoController) showDemo(ctx *gin.Context) (any, error) {
return ctr.demoSvc.Show()
}
func (ctr *demoController) error(ctx *gin.Context) (any, error) {
return ctr.demoSvc.Error()
}
func (ctr *demoController) echo(ctx *gin.Context) (any, error) {
type Req struct {
Echo string `form:"echo"`
}
var req Req
if err := ctx.Bind(&req); err != nil {
return nil, gin.NewParameterError(err.Error(), utils.ParameterParseError)
}
return ctr.demoSvc.Echo(req.Echo)
}
# Service
规范上,我们要求将服务的接口定义在internal/interface/service
目录,文件名以i_
打头,接口类型以I
打头,例如:
文件:i_demo.go
package service
import "web-app/internal/interface/domain"
type IDemo interface {
Show() (*domain.DemoEntity, error)
Echo(input string) (string, error)
Error() (any, error)
}
服务的逻辑实现,放到internal/module
目录分模块实现。
# 数据库操作
在下一个例子中,我们将构建使用MySQL作为数据的Web项目。