在以前的文章中,我们创建了一个HTTP REST API server和一个CLI。
创建一个直接称为我们API的CLI很酷,但是一个好习惯是在用户和API之间具有抽象(SDK,客户端库)。从头开始创建SDK并不像您想象的那么容易。如果我告诉您可以使用发电机从我们的REST API中创建我们的SDK,而更准确地说是从我们的开放API规格中创建我们的SDK?
让我们的go!
SDK
让我们通过谈论API和SDK来开始本文。
拥有API很酷,但是如果您没有SDK(客户端库),它们是用户和API之间的抽象层,您的用户必须心脏了解API的体系结构,并且每次API都会发展,他们也需要在一边更改应用程序。
您还可以为您的应用程序,下文,产品,服务提供多个SDK,一种通过您的社区使用的编程语言。
OpenAPI发电机
如果您在Internet上进行搜索,您会发现有几种工具允许在纸上从Swagger文件中生成SDK/A客户端库。每个工具都有其优点和缺点。对于我们的SDK,我们将使用研究期间最多的工具,并记录下来,具有目标语言列表,并具有现成的CLI:OpenAPI Generator。
简而言之,OpenAPI Generator允许生成API客户端库(SDK生成),服务器存根,文档和配置自动给出了OpenAPI Spec(V2,V3)。
使用50+ client generators,对于各种编程语言,可以生成代码与任何曝光OpenAPI文档的服务器进行交互。
OpenAPI发电机是开源的,并具有GitHub repository。
在本文中,我们将使用允许我们生成GO SDK的GO Generator,并使用参数定义。
停止Blahblah,让我们安装OpenAPI Generator。
来自来源的几个installation options of the CLI exists,docker,npm ...
让我们从Brew中安装CLI:
$ brew install openapi-generator
让我们检查CLI在本地正确安装:
$ openapi-generator version
6.6.0
我们想要什么?
很酷,我们已经安装了发电机,但是我们想做什么?
我们有一个漂亮的api Gophers API。
这个简单的API处理可爱的Gophers,并将您带到您:
- 列出现有的地鼠
- 显示有关Gopher的信息
- 创建一个新的Gopher
- 删除Gopher
- 更新Gopher的路径和URL
您如何从这个不错的API开始并在GO中创建SDK? ð
初始化
首先,我们可以在GitHub中创建我们的存储库(为了共享和开源)。
为此,我登录GitHub website,单击“存储库”链接,单击“新”绿色按钮,然后创建了一个名为“gophers-sdk-go”的新存储库。
现在,在您的本地计算机中,git克隆这个新的存储库您想要:
$ git clone https://github.com/scraly/gophers-sdk-go.git
$ cd gophers-sdk-go
现在,我们必须初始化GO模块(依赖关系管理):
$ go mod init github.com/scraly/gophers-sdk-go
go: creating new go.mod: module github.com/scraly/gophers-sdk-go
这将创建一个这样的go.mod
文件:
module github.com/scraly/gophers-sdk-go
go 1.19
OpenAPI定义
我们找到了一个很酷的工具来从招摇文件/OpenAPI定义生成SDK,所以让我们看看specs definition of our Gophers API:
consumes:
- application/json
info:
description: HTTP server that handle cute Gophers.
title: gophers-api
version: 0.1.0
produces:
- application/json
host: localhost:8080
schemes:
- http
swagger: "2.0"
tags:
- name: gophers
description: Handle Gophers
paths:
/healthz:
get:
description: Check Health
tags:
- gophers
operationId: checkHealth
produces:
- text/plain
responses:
'200':
description: OK message.
headers:
Access-Control-Allow-Origin:
type: string
schema:
type: string
enum:
- OK
/gophers:
get:
description: List Gophers
tags:
- gophers
produces:
- application/json
responses:
200:
description: Return the Gophers list.
headers:
Access-Control-Allow-Origin:
type: string
schema:
type: array
items:
$ref: '#/definitions/Gopher'
/gopher:
post:
summary: Add a new Gopher
tags:
- gophers
consumes:
- application/json
parameters:
- in: body
name: gopher
description: The Gopher to create.
schema:
type: object
required:
- name
- displayname
- url
properties:
name:
type: string
displayname:
type: string
url:
type: string
responses:
201:
description: Created
schema:
type: object
$ref: '#/definitions/Gopher'
409:
description: Gopher already exists
get:
description: Get a gopher by a given name
tags:
- gophers
produces:
- application/json
parameters:
- name: name
in: query
type: string
required: true
description: Gopher name
responses:
200:
description: A gopher
headers:
Access-Control-Allow-Origin:
type: string
schema:
type: object
$ref: '#/definitions/Gopher'
404:
description: A gopher with the specified Name was not found.
headers:
Access-Control-Allow-Origin:
type: string
delete:
description: Delete a gopher by a given name
tags:
- gophers
parameters:
- name: name
in: query
type: string
required: true
description: Gopher name
responses:
200:
description: OK
404:
description: A gopher with the specified Name was not found.
put:
description: Update a gopher
tags:
- gophers
parameters:
- in: body
name: gopher
description: The Gopher to update.
schema:
type: object
required:
- name
- displayname
- url
properties:
name:
type: string
displayname:
type: string
url:
type: string
responses:
200:
description: Updated
schema:
type: object
$ref: '#/definitions/Gopher'
404:
description: A gopher with the specified Name was not found.
definitions:
Gopher:
type: object
properties:
name:
type: string
example: my-gopher
displayname:
type: string
example: My Gopher
url:
type: string
example: https://raw.githubusercontent.com/scraly/gophers/main/arrow-gopher.png
有用的提示
为了使我们的SDK使用正确的信息生成,我们必须小心地定义Swagger File的某些元素。
因此,我们将遵循我们的“ Yoda Gopher”的建议,看看要遵循什么技巧。
您将拥有的主机/服务器
默认情况下,发电机认为在没有定义端口的Localhost中可以访问API,因此请定义带有URL的主机或服务器,该URL允许访问您的API。这些字段由发电机读取。
因此,当用户/一个开发人员使用您的SDK时,它将直接访问API。
在我身边,您可以在Swagger文件中看到,我使用的是host
字段等于localhost:8080
。
因此,我的API必须在端口8080上本地运行,以便通过SDK进行调用。
您将定义的标签
如果我们不在Swagger文件中定义标签,则生成的SDK将具有API名称:DefaultApi
。就我个人而言,我认为与DefaultApi
拥有SDK并不好。因此,我们可以指定标签,并且由于标签,发电机将在您的方便下命名您的API。
例如,通过定义和使用gophers
标签,生成器将生成API名称GophersApi
,它比DefaultApi
^^。
操作ID您不会忘记
一些发电机,包括OpenAPI Generator,使用operationId
字段来命名方法。每个operationId
必须在Swagger File中的所有操作中都是唯一的。
检查规格的有效性
在进行进一步之前,我们可以检查我们的Swagger文件是否对发电机有效。
$ openapi-generator validate -i https://raw.githubusercontent.com/scraly/gophers-api/main/pkg/swagger/swagger.yml
Validating spec (https://raw.githubusercontent.com/scraly/gophers-api/main/pkg/swagger/swagger.yml)
No validation issues detected.
完美。
配置应用程序
我们将使用将为我们创建并修改一堆文件的生成器。为了告诉工具不要修改或删除某些文件,OpenAPI Generator支持.openapi-generator-ignore
文件。就像.gitignore
或.dockerignore
fileð。
让我们创建一个.openapi-generator-ignore
文件,以便告诉OpenAPI Generator不要生成或编辑go.mod
,go.sum
和LICENSE
文件,因为我们已经使用go mod init
命令和github中的许可证文件创建它们:
# OpenAPI Generator Ignore
go.mod
go.sum
LICENSE
创建我们的应用程序
让我们使用OpenAPI Generator CLI生成我们的SDK:
$ openapi-generator generate \
-i https://raw.githubusercontent.com/scraly/gophers-api/main/pkg/swagger/swagger.yml \
-g go \
--additional-properties packageName=gopherssdkgo,packageVersion=0.0.4,useTags=true \
--git-user-id scraly \
--git-repo-id gophers-sdk-go
一个好习惯是定义我们想要的所有参数以根据我们的需要生成SDK。
让我们解释一下我们的定义:
-
-i
(--input-spec
)参数允许您定义Swagger/OpenAPI定义文件的源 -
-g
(--generator-name
)参数允许您定义所需的SDK。在这里,我们想要一个SDK -
--additional-properties
参数允许您自定义生成的go sdk,并根据您的需求:包装的名称,包装的版本,如果您想在“ Abounding Swagger/Openapi”标签中使用我们之前谈论的wim -lim ... li> - 默认情况下,SDK将生成这样的导入行:
openapiclient "github.com/GIT_USER_ID/GIT_REPO_ID"
。--git-user-id
和--git-repo-id
将允许您使用git存储库进行自定义。
/!\不要使用“ - ”,“ _”或packageName
字段中的非字母字符,当您使用sdkð:
时,您将有一个奇怪的错误消息
$ go run sample.go
# github.com/scraly/gophers-sdk-go
../../../../go/pkg/mod/github.com/scraly/gophers-sdk-go@v0.0.0-20230716090011-35a148834c43/api_gophers.go:11:16: syntax error: unexpected -, expected semicolon or newline
有关更多信息,您可以查看official documentation about package name in Go。
请转到OpenAPI Generator documentation了解Generate命令的所有可能参数。
请注意,如果您想了解更多有关要配置和使用的可能参数的信息,请查看Go generator documentation。
在此执行命令时,您将拥有的输出:
[main] INFO o.o.codegen.DefaultGenerator - Generating with dryRun=false
[main] INFO o.o.codegen.DefaultGenerator - OpenAPI Generator: go (client)
[main] INFO o.o.codegen.DefaultGenerator - Generator 'go' is considered stable.
[main] INFO o.o.c.languages.AbstractGoCodegen - Environment variable GO_POST_PROCESS_FILE not defined so Go code may not be properly formatted. To define it, try `export GO_POST_PROCESS_FILE="/usr/local/bin/gofmt -w"` (Linux/Mac)
[main] INFO o.o.c.languages.AbstractGoCodegen - NOTE: To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI).
[main] INFO o.o.codegen.InlineModelResolver - Inline schema created as _gopher_put_request. To have complete control of the model name, set the `title` field or use the inlineSchemaNameMapping option (--inline-schema-name-mappings in CLI).
[main] INFO o.o.codegen.TemplateManager - writing file ./model_gopher.go
[main] INFO o.o.codegen.TemplateManager - writing file ./docs/Gopher.md
[main] INFO o.o.codegen.TemplateManager - writing file ./model__gopher_put_request.go
[main] INFO o.o.codegen.TemplateManager - writing file ./docs/GopherPutRequest.md
[main] WARN o.o.codegen.DefaultCodegen - Empty operationId found for path: get /gophers. Renamed to auto-generated operationId: gophersGet
[main] WARN o.o.codegen.DefaultCodegen - Empty operationId found for path: get /gopher. Renamed to auto-generated operationId: gopherGet
[main] WARN o.o.codegen.DefaultCodegen - Empty operationId found for path: put /gopher. Renamed to auto-generated operationId: gopherPut
[main] WARN o.o.codegen.DefaultCodegen - Empty operationId found for path: post /gopher. Renamed to auto-generated operationId: gopherPost
[main] WARN o.o.codegen.DefaultCodegen - Empty operationId found for path: delete /gopher. Renamed to auto-generated operationId: gopherDelete
[main] INFO o.o.codegen.TemplateManager - writing file ./api_gophers.go
[main] INFO o.o.codegen.TemplateManager - Skipped ./test/api_gophers_test.go (Test files never overwrite an existing file of the same name.)
[main] INFO o.o.codegen.TemplateManager - writing file ./docs/GophersApi.md
[main] INFO o.o.codegen.TemplateManager - writing file ./api/openapi.yaml
[main] INFO o.o.codegen.TemplateManager - writing file ./README.md
[main] INFO o.o.codegen.TemplateManager - writing file ./git_push.sh
[main] INFO o.o.codegen.TemplateManager - writing file ./.gitignore
[main] INFO o.o.codegen.TemplateManager - writing file ./configuration.go
[main] INFO o.o.codegen.TemplateManager - writing file ./client.go
[main] INFO o.o.codegen.TemplateManager - writing file ./response.go
[main] INFO o.o.codegen.TemplateManager - Ignored ./go.mod (Ignored by rule in ignore file.)
[main] INFO o.o.codegen.TemplateManager - Ignored ./go.sum (Ignored by rule in ignore file.)
[main] INFO o.o.codegen.TemplateManager - writing file /Users/aurelievache/git/github.com/scraly/gophers-sdk-go/./.travis.yml
[main] INFO o.o.codegen.TemplateManager - writing file ./utils.go
[main] INFO o.o.codegen.TemplateManager - Skipped ./.openapi-generator-ignore (Skipped by supportingFiles options supplied by user.)
[main] INFO o.o.codegen.TemplateManager - writing file ./.openapi-generator/VERSION
[main] INFO o.o.codegen.TemplateManager - writing file ./.openapi-generator/FILES
################################################################################
# Thanks for using OpenAPI Generator. #
# Please consider donation to help us maintain this project 🙏 #
# https://opencollective.com/openapi_generator/donate #
################################################################################
很酷,已经生成了SDK!
命令为您生成了有用的文件:
.
├── LICENSE
├── README.md
├── api
│ └── openapi.yaml
├── api_gophers.go
├── client.go
├── configuration.go
├── docs
│ ├── Gopher.md
│ ├── GopherPutRequest.md
│ └── GophersApi.md
├── git_push.sh
├── go.mod
├── go.sum
├── model__gopher_put_request.go
├── model_gopher.go
├── response.go
├── sample
│ └── sample.go
├── test
│ └── api_gophers_test.go
└── utils.go
您可以看到,发电机还生成了文档文件,例如README.md
和docs
文件夹,它们将帮助您知道如何使用新的新SDK。
现在,我们可以在GITHUB中推动我们生成的SDK的第一个版本。
$ git add .
$ git commit -m "feat: first version of the generated go sdk" *
$ git push
好练习
再次,让我们听我们的YodaGopherð。
一个好的做法是创建一个标签(和版本)您的SDK的每个新版本。它将帮助用户获得您已发行/发布SDK的最新版本(或想要的版本)。
让我们测试它
啊,我喜欢这一刻,我们将能够测试我们创建的内容(最终生成^^)。
在本地运行API
首先,正如我们所看到的,Swagger/OpenAPI规格定义了我们的Gophers API在localhost:8080
中运行,因此我们需要在本地运行。
克隆地鼠API存储库:
$ git clone https://github.com/scraly/gophers-api.git
$ cd gophers-api
当我们在Taskfile中定义任务以自动化我们的常见任务,例如以前的文章,我们只需要执行task run
命令即可在localhost中启动API:8080:
$ task run
task: [run] GOFLAGS=-mod=mod go run internal/main.go
2023/07/16 11:53:35 Serving gophers API at http://[::]:8080
让我们测试我们的API。是的,对不起,但是我喜欢测试项目的每个步骤^^。
$ curl localhost:8080/gophers
[{"displayname":"5th Element","name":"5th-element","url":"https://raw.githubusercontent.com/scraly/gophers/main/5th-element.png"}]
使用和测试我们的SDK
现在,我们有一个跑步和发布/发布的GO SDK为我们的Gophers,让我们创建一个小样本,可以测试2个API调用:
/healthz
/gophers
让我们创建一个带有以下内容的sample.go
文件:
package main
import (
"context"
"fmt"
"os"
gopherssdk "github.com/scraly/gophers-sdk-go"
)
func main() {
config := gopherssdk.NewConfiguration()
client := gopherssdk.NewAPIClient(config)
// Check Health
// When we call GophersApi.CheckHealth method, it return a string
// equals to OK if the Gophers API is running and healthy
health, healthRes, healthErr := client.GophersApi.CheckHealth(context.Background()).Execute()
if healthErr != nil {
fmt.Fprintf(os.Stderr, "Error when calling `GophersApi.CheckHealth``: %v\n", healthErr)
fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", healthRes)
}
// response from `CheckHealth`: string
fmt.Fprintf(os.Stdout, "Response from `GophersApi.CheckHealth`: %v\n", health)
// Get Gophers
gophers, gophersRes, GophersErr := client.GophersApi.GophersGet(context.Background()).Execute()
if GophersErr != nil {
fmt.Fprintf(os.Stderr, "Error when calling `GophersApi.GophersGet``: %v\n", GophersErr)
fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", gophersRes)
}
// response from `GophersGet`: []Gopher
if gophersRes.StatusCode == 200 {
// Get and display all existing Gophers
fmt.Println("Response from `GophersApi.GophersGet`:")
fmt.Println("Number of Gophers:", len(gophers))
for _, myGopher := range gophers {
fmt.Println("DisplayName: ", *myGopher.Displayname)
fmt.Println("Name:", *myGopher.Name)
fmt.Println("URL:", *myGopher.Url)
}
}
}
在此文件中:
- 导入GO SDK ^^
- 使用新的配置启动客户
- 调用
GophersApi.CheckHealth
调用/healthz
路线并显示结果 的方法
- 调用
GophersApi.GophersGet
调用/gophers
路线并显示返回的地鼠的列表
让我们测试它:
$ go run sample.go
Response from `GophersApi.CheckHealth`: OK
Response from `GophersApi.GophersGet`:
Number of Gophers: 1
DisplayName: 5th Element
Name: 5th-element
URL: https://raw.githubusercontent.com/scraly/gophers/main/5th-element.png
酷!我们正在使用GO SDK来调用我们的API在Localhost:8080(不知道API的体系结构)! ð
下一步是什么
在本文中,我们看到了如何从Swagger File/OpenAPI Specs生成GO SDK。
但是,当我们的招摇文件更改时会发生什么?
一个想法可以是在我们的Swagger File/OpenAPI规格更改的所有更改中自动再生我们的SDK。
由于我们的API和SDK托管在GitHub中,因此可以使用GitHub Actionð。
固定此自动化。例如,我们可以考虑使用Hook workflow_dispatch
,它允许更改一个存储库来触发其他存储库中的操作。
结论
正如您在本文和以前的文章中所看到的那样,可以在GO中创建应用程序:CLI,REST API ...以及使用有用的工具,可以帮助我们创建SDK。
我们应用程序的所有代码均可在以下方式提供:https://github.com/scraly/gophers-sdk-go
也可以使用该文档:https://pkg.go.dev/github.com/scraly/gophers-sdk-go
在以下文章中,我们将在GO中创建其他应用程序/类型的应用程序。
希望您会喜欢的。