前面我们介绍的命令行管理,都是通过回调函数的parser
对象获取解析的参数及选项数据,在使用的时候存在以下痛点:
目前成都创新互联公司已为1000多家的企业提供了网站建设、域名、雅安服务器托管、网站托管、服务器租用、企业网站设计、嘉祥网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。
我们来一个最简单的结构化管理参数示例。我们将前面介绍过的Command
示例改造为结构化管理:
package main
import (
"context"
"fmt"
"github.com/GOgf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/gctx"
)
type cMain struct {
g.Meta `name:"main"`
}
type cMainHttpInput struct {
g.Meta `name:"http" brief:"start http server"`
}
type cMainHttpOutput struct{}
type cMainGrpcInput struct {
g.Meta `name:"grpc" brief:"start grpc server"`
}
type cMainGrpcOutput struct{}
func (c *cMain) Http(ctx context.Context, in cMainHttpInput) (out *cMainHttpOutput, err error) {
fmt.Println("start http server")
return
}
func (c *cMain) Grpc(ctx context.Context, in cMainGrpcInput) (out *cMainGrpcOutput, err error) {
fmt.Println("start grpc server")
return
}
func main() {
cmd, err := gcmd.NewFromObject(cMain{})
if err != nil {
panic(err)
}
cmd.Run(gctx.New())
}
可以看到,我们通过对象的形式来管理父级命令,通过方法的形式来管理其下一层级的子级命令,并通过规范化的Input
输入参数对象来定义子级命令的描述/参数/选项。大部分场景下,大家可以忽略Output
返回对象的使用,但为规范化及扩展性需要保留,如果未用到,该返回参数直接返回nil
即可。关于其中的结构体标签,后续会有介绍。
我们将示例代码编译后,执行查看效果:
$ main
USAGE
main COMMAND [OPTION]
COMMAND
http start http server
grpc start grpc server
DESCRIPTION
this is the command entry for starting your process
使用http
命令:
$ main http
start http server
使用grpc
命令:
$ main grpc
start grpc server
效果和前面介绍的示例一致。
既然命令行通过对象化管理,我们仔细看看参数/选项是如何通过结构化管理。
我们将上面的实例简化一下,来个简单的例子,实现通过http
命令开启http
服务:
package main
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gcmd"
"github.com/gogf/gf/v2/os/gctx"
)
type cMain struct {
g.Meta `name:"main" brief:"start http server"`
}
type cMainHttpInput struct {
g.Meta `name:"http" brief:"start http server"`
Name string `v:"required" name:"NAME" arg:"true" brief:"server name"`
Port int `v:"required" short:"p" name:"port" brief:"port of http server"`
}
type cMainHttpOutput struct{}
func (c *cMain) Http(ctx context.Context, in cMainHttpInput) (out *cMainHttpOutput, err error) {
s := g.Server(in.Name)
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Write("Hello world")
})
s.SetPort(in.Port)
s.Run()
return
}
func main() {
cmd, err := gcmd.NewFromObject(cMain{})
if err != nil {
panic(err)
}
cmd.Run(gctx.New())
}
我们为http
命令定义了两个输入参数:
NAME
服务的名称,通过参数输入。这里使用了大写形式,方便展示在自动生成的帮助信息中port
服务的端口,通过p/port
选项输入并且我们通过v:"required"
校验标签为这两个参数都绑定的必需的校验规则。是的,在GoFrame
框架中,只要涉及到校验的地方都使用了统一的校验组件。
我们编译后执行看看效果:
$ main http
arguments validation failed for command "http": The Name field is required
1. arguments validation failed for command "http"
1). github.com/gogf/gf/v2/os/gcmd.newCommandFromMethod.func1
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/os/gcmd/gcmd_command_object.go:290
2). github.com/gogf/gf/v2/os/gcmd.(*Command).doRun
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/os/gcmd/gcmd_command_run.go:120
3). github.com/gogf/gf/v2/os/gcmd.(*Command).RunWithValueError
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/os/gcmd/gcmd_command_run.go:77
4). github.com/gogf/gf/v2/os/gcmd.(*Command).RunWithValue
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/os/gcmd/gcmd_command_run.go:32
5). github.com/gogf/gf/v2/os/gcmd.(*Command).Run
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/os/gcmd/gcmd_command_run.go:26
6). main.main
/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.test/test.go:38
2. The Name field is required
执行后,报错了,这个错误来自于数据校验,表示必须参数(Name/Port
)必须传递。
这里的报错打印了堆栈信息,因为GoFrame
框架采用了全错误堆栈设计,所有组件错误都会带有自底向上的错误堆栈,以方便错误快速定位。当然我们可以通过RunWithError
方法获取返回的错误对象关闭堆栈信息。
我们增加参数输入再试试:
$ main http my-http-server -p 8199
2022-01-19 22:52:45.808 [DEBU] openapi specification is disabled
SERVER | DOMAIN | ADDRESS | METHOD | ROUTE | HANDLER | MIDDLEWARE
-----------------|---------|---------|--------|-------|-----------------------------------------------------------------|--------------------
my-http-server | default | :8199 | ALL | / | main.(*cMain).Http.func1 |
-----------------|---------|---------|--------|-------|-----------------------------------------------------------------|--------------------
my-http-server | default | :8199 | ALL | /* | github.com/gogf/gf/v2/net/ghttp.internalMiddlewareServerTracing | GLOBAL MIDDLEWARE
-----------------|---------|---------|--------|-------|-----------------------------------------------------------------|--------------------
2022-01-19 22:52:45.810 66292: http server started listening on [:8199]
是的,这就对了。
GoFrame
框架的开发工具大量使用了对象化、结构化的命令行管理,大家感兴趣可以更进一步查看源码了解:https://github.com/gogf/gf-cli
在结构化设计中,我们使用了一些结构体标签,大部分来源于Command
命令的属性,这里我们来介绍一下:
标签 | 缩写 | 是否必须 | 说明 | 注意事项 |
name |
- | 是 | 命名名称 | |
short |
- | - | 命令缩写 | |
usage |
- | - | 命令使用 | |
brief |
- | - | 命令描述 | |
age |
- | - | 表示该输入参数来源于参数而不是选项 | 仅用于属性标签 |
orphan |
- | - | 表示该选项不带参数 | 属性通常为bool 类型 |
description |
dc | - | 命令的详细介绍 | |
additional |
ad | - | 命令的额外描述信息 | |
examples |
eg | - | 命令的使用示例 | |
root |
- | - | 表示统一结构体方法中,该方法是父级命令,其他方法是它的子级命令 | 仅用于Meta 标签 |
strict |
- | - | 表示该命令严格解析参数/选项,当输入不支持的参数/选项时,返回错误 | 仅用于Meta 标签 |
config |
- | - | 表示该命令的选项数据支持从指定的配置读取,配置来源于默认的全局单例配置对象 | 仅用于Meta 标签 |
结构化的参数输入支持自动的数据类型转换,您只需要定义好数据类型,其他的事情交给框架组件即可。自动数据类型转换出现在框架的很多组件中,特别是HTTP/GRPC
服务的参数输入中。底层数据转换组件使用的是类型转换
同样的,数据校验组件也是使用的统一的组件。
当命令行中没有传递对应的数据时,输入参数的结构体数据支持从配置组件中自动获取,只需要在Meta
中设置config
标签即可,配置来源于默认的全局单例配置对象。具体示例可以参考GoFrame
框架开发工具源码:https://github.com/gogf/gf-cli
网页标题:创新互联GoFrame教程:GoFrame命令管理-结构化参数
URL分享:http://www.shufengxianlan.com/qtweb/news2/57502.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联