Skip to content

服务 CLI#

Huma 内置了一个轻量级工具,用于为您的服务包装 CLI,使您能够使用不同的参数运行它,并轻松编写自定义命令来执行诸如打印 OpenAPI 或按需运行数据库迁移等操作。

CLI 选项使用类似于输入和输出结构体的策略,使您能够使用相同的模式对命令行参数进行验证和文档化。它在底层使用 Cobra,支持自定义命令,并包括自动环境变量绑定等功能。

main.go
// First, define your input options.
type Options struct {
	Debug bool   `doc:"Enable debug logging"`
	Host  string `doc:"Hostname to listen on."`
	Port  int    `doc:"Port to listen on." short:"p" default:"8888"`
}

func main() {
	// Then, create the CLI.
	cli := humacli.New(func(hooks humacli.Hooks, opts *Options) {
		fmt.Printf("I was run with debug:%v host:%v port%v\n",
			opts.Debug, opts.Host, opts.Port)
	})

	// Run the thing!
	cli.Run()
}

You can then run the CLI and see the results:

Terminal
// Run with defaults
$ go run main.go
I was run with debug:false host: port:8888

// Run with options
$ go run main.go --debug=true --host=localhost --port=8000
I was run with debug:true host:localhost port:8000

To do useful work, you will want to register a handler for the default start command and optionally a way to gracefully shutdown the server:

main.go
cli := humacli.New(func(hooks humacli.Hooks, opts *Options) {
	// Set up the router and API
	// ...

	// Create the HTTP server.
	server := http.Server{
		Addr:    fmt.Sprintf(":%d", options.Port),
		Handler: router,
	}

	hooks.OnStart(func() {
		// Start your server here
		server.ListenAndServe()
	})

	hooks.OnStop(func() {
		// Gracefully shutdown your server here
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		server.Shutdown(ctx)
	})
})

命名

选项字段会自动转换为 --kebab-casing 以在命令行中使用。如果您想使用不同的名称,请使用 name 结构体标签来覆盖默认行为!

传递选项#

选项可以显式作为命令行参数传递给服务,或者通过以 SERVICE_ 为前缀的环境变量提供。例如,要在端口 8000 上运行服务:

# 示例:传递命令行参数
$ go run main.go --port=8000

# 也支持简短参数
$ go run main.go -p 8000

# 示例:通过环境变量传递
$ SERVICE_PORT=8000 go run main.go

优先级

如果同时存在环境变量和命令行参数,则命令行参数优先。

自定义选项#

自定义选项通过向选项结构体添加字段来定义。支持以下类型:

类型 示例输入
bool true, false
int / int64 1234, 5, -1
string prod, http://api.example.tld/
time.Duration 500ms, 3s, 1h30m

可用的结构体标签如下:

标签 描述 示例
default 默认值(自动解析) default:"123"
doc 描述该选项 doc:"Who to greet"
name 覆盖选项名称 name:"my-option-name"
short 选项的单字母简短名称 short:"p" for -p

以下是使用它们的示例:

main.go
type Options struct {
	Debug bool   `doc:"Enable debug logging"`
	Host  string `doc:"Hostname to listen on."`
	Port  int    `doc:"Port to listen on." short:"p" default:"8888"`
}

自定义命令#

您可以通过 cli.Root() 访问根 cobra.Command,并通过 cli.Root().AddCommand(...) 添加新的自定义命令。例如,要有一个命令打印生成的 OpenAPI:

main.go
var api huma.API

// ... set up the CLI, create the API wrapping the router ...

cli.Root().AddCommand(&cobra.Command{
	Use:   "openapi",
	Short: "Print the OpenAPI spec",
	Run: func(cmd *cobra.Command, args []string) {
		b, err := api.OpenAPI().YAML()
		if err != nil {
			panic(err)
		}
		fmt.Println(string(b))
	},
})

注意

您可以使用 api.OpenAPI().DowngradeYAML() 来输出 OpenAPI 3.0 而非 3.1,以供不支持 3.1 的工具使用。

现在您可以运行您的服务并使用新命令:go run . openapi。请注意,它不会启动服务器;它只会运行您的命令处理程序代码。一些自定义命令的想法:

  • 打印 OpenAPI 规范
  • 打印 JSON Schemas
  • 运行数据库迁移
  • 运行客户场景测试
  • 将常见操作打包成单个实用命令,例如添加新用户

带有选项的自定义命令#

如果您想在自定义命令中使用您的自定义选项结构体,请使用 huma.WithOptions(func(cmd *cobra.Command, args []string, options *YourOptions)) func(cmd *cobra.Command, args []string) 实用函数。它确保在运行您的命令之前解析并提供选项。

更多自定义

您也可以覆盖 cli.Root().Run 来完全自定义运行服务器的方式。或者干脆放弃 cli 包!

应用名称和版本#

您可以设置在帮助输出和版本命令中使用的应用名称和版本。默认情况下,应用名称是二进制文件的名称,版本未设置。您可以使用根 cobra.CommandUseVersion 字段来设置它们:

main.go
// cli := humacli.New(...)

cmd := cli.Root()
cmd.Use = "appname"
cmd.Version = "1.0.1"

cli.Run()

然后您将看到类似以下内容:

Terminal
$ go run ./demo --help
Usage:
  appname [flags]

Flags:
  -h, --help            help for appname
  -p, --port int         (default 8888)
  -v, --version         version for appname

$ go run ./demo --version
appname version 1.0.1

深入了解#