生成客户端

生成代码

API 文档通常足够使用者使用。但是在微服务场景下,服务之间也会存在调用关系。因此需要使调用者方便快速的进行 API 调用,可以生成客户端以供使用:

  1. $ nirvana client

该命令默认会在 ./client 目录下生成 golang 客户端代码,可以通过 --output 指定其他输出目录(目前仅支持生成 golang 客户端,其他语言客户端尚不支持)。

输出日志如下(项目路径和日志时间会有所不同):

  1. INFO 0702-15:50:54.156+08 client.go:73 | No packages are specified, defaults to pkg/apis
  2. INFO 0702-15:50:55.609+08 client.go:81 | Project root directory is /home/go/src/myproject
  3. INFO 0702-15:50:55.610+08 client.go:106 | Generated golang client package myproject/client

生成的客户端代码如下:

  1. client
  2. ├── client.go
  3. └── v1
  4. ├── client.go
  5. └── types.go

./client.go 生成代码:

  1. package client
  2. import (
  3. v1 "myproject/client/v1"
  4. rest "github.com/caicloud/nirvana/rest"
  5. )
  6. // Interface describes a versioned client.
  7. type Interface interface {
  8. // V1 returns v1 client.
  9. V1() v1.Interface
  10. }
  11. // Client contains versioned clients.
  12. type Client struct {
  13. v1 *v1.Client
  14. }
  15. // NewClient creates a new client.
  16. func NewClient(cfg *rest.Config) (Interface, error) {
  17. c := &Client{}
  18. var err error
  19. c.v1, err = v1.NewClient(cfg)
  20. if err != nil {
  21. return nil, err
  22. }
  23. return c, nil
  24. }
  25. // MustNewClient creates a new client or panic if an error occurs.
  26. func MustNewClient(cfg *rest.Config) Interface {
  27. return &Client{
  28. v1: v1.MustNewClient(cfg),
  29. }
  30. }
  31. // V1 returns a versioned client.
  32. func (c *Client) V1() v1.Interface {
  33. return c.v1
  34. }

./v1/client.go 生成代码

  1. package v1
  2. import (
  3. "context"
  4. rest "github.com/caicloud/nirvana/rest"
  5. )
  6. // Interface describes v1 client.
  7. type Interface interface {
  8. // GetMessage return a message by id.
  9. GetMessage(ctx context.Context, message int) (message1 *Message, err error)
  10. // ListMessages returns all messages.
  11. ListMessages(ctx context.Context, count int) (messages []Message, err error)
  12. }
  13. // Client for version v1.
  14. type Client struct {
  15. rest *rest.Client
  16. }
  17. // NewClient creates a new client.
  18. func NewClient(cfg *rest.Config) (*Client, error) {
  19. client, err := rest.NewClient(cfg)
  20. if err != nil {
  21. return nil, err
  22. }
  23. return &Client{client}, nil
  24. }
  25. // MustNewClient creates a new client or panic if an error occurs.
  26. func MustNewClient(cfg *rest.Config) *Client {
  27. client, err := NewClient(cfg)
  28. if err != nil {
  29. panic(err)
  30. }
  31. return client
  32. }
  33. // GetMessage return a message by id.
  34. func (c *Client) GetMessage(ctx context.Context, message int) (message1 *Message, err error) {
  35. message1 = new(Message)
  36. err = c.rest.Request("GET", 200, "/apis/v1/messages/{message}").
  37. Path("message", message).
  38. Data(message1).
  39. Do(ctx)
  40. return
  41. }
  42. // ListMessages returns all messages.
  43. func (c *Client) ListMessages(ctx context.Context, count int) (messages []Message, err error) {
  44. err = c.rest.Request("GET", 200, "/apis/v1/messages").
  45. Query("count", count).
  46. Data(&messages).
  47. Do(ctx)
  48. return
  49. }

./v1/types.go 生成代码:

  1. package v1
  2. // Message describes a message entry.
  3. type Message struct {
  4. ID int `json:"id"`
  5. Title string `json:"title"`
  6. Content string `json:"content"`
  7. }

生成的客户端是版本化的,版本在 nirvana.yaml 中定义。API 依赖的结构体都会被提取出来并生成到 types.go 文件中,方便客户端使用。

每个服务都可以生成一份客户端,为了方便使用,可以将客户端整合在一起。具体内容请参考 多客户端整合

注意事项

函数名称

生成的 client 函数的名称会优先使用对应 DefinitionSummary 字段,将其去掉空格拼接起来,比如 SummaryCreate XX,则函数名称为 CreateXX;若 Summary 字段为空,会使用 DefinitionFunction 名称,若 Function 是匿名函数,则会使用 AnonymousAPI

API types 定义

不要在 struct 里面嵌套匿名 struct:

BadGood
go type XXObject struct { AA struct { Test string `json:"test"` } `json:"aa"` }go type XXObject struct { AA AA `json:"aa"` } type AA struct { Test string `json:"test"` }

使用客户端

客户端的使用非常简单,只需要创建客户端,然后调用相应的 API 函数即可:

  1. func main() {
  2. cli := client.MustNewClient(&rest.Config{
  3. Scheme: "http",
  4. Host: "localhost:8080",
  5. })
  6. msgs, err := cli.V1().ListMessages(context.Background(), 10)
  7. if err != nil {
  8. log.Fatal(err)
  9. }
  10. log.Info(msgs)
  11. }

限制

目前客户端生成对接口的支持还不完善,仅仅对 io.Readerio.ReadCloser 支持良好,因此在 API 的参数和返回值中,尽量避免使用其他接口。