# - go-swagger 工具為 Go 程序生成 swagger 相關文檔 (手動配yaml文件)
# 從Go 1.17開始,不贊成使用Go get安裝可執行文件建議使用 go install
go install github.com/go-swagger/go-swagger/cmd/swagger@latest
# 不建議
# go get -u github.com/go-swagger/go-swagger/cmd/swagger

# - swag 工具將 Go 注釋轉換為 Swagger 文檔 2.0,使您可以快速與現有的 Go 項目集成(使用 Swagger UI,此處用于go-gin項目
go install github.com/swaggo/swag/cmd/swag@latest
# go get -u github.com/swaggo/swag/cmd/swag

除此此外我們還可以手動編譯:

cd D:\Study\Go\package\pkg\mod\github.com\swaggo\swag@v1.16.1\cmd\swag
go build -o swag.exe ./main.go

2.此處由于我是 windows 其安裝目錄如下, 通過CMD命令或者PowerShell查看.

# CMD
> dir "%GOPATH%/bin/" /B
swag
swagger.exe

# PowerShell
> ls $env:GOPATH/bin
Directory: D:\Study\Go\package\bin
-a--- 2023/6/12 14:59 14539264 swag.exe
-a--- 2023/6/13 9:49 24419840 swagger.exe

其他安裝方式,例如二進制方式安裝或者容器方式安裝。

Docker 安裝方式

# 鏡像拉取
docker pull quay.io/goswagger/swagger
# 在 Winwdows 中即在Go開發項目下執行
docker run --rm -it --env GOPATH=/go -v %CD%:/go/src -w /go/src quay.io/goswagger/swagger
# 在 Linux 中
docker run --rm -it --env GOPATH=/go -v $pwd:/go/src -w /go/src quay.io/goswagger/swagger

3. 如何在Gin使用Swagger生成API接口文檔?

1. 在你的Go-gin項目main.go代碼中的?package main?下添加通用API注釋, 注釋解析。

package main

import (
"errors"
"net/http"

"github.com/gin-gonic/gin"
"github.com/swaggo/swag/example/celler/controller"
_ "github.com/swaggo/swag/example/celler/docs"
"github.com/swaggo/swag/example/celler/httputil"

swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)

// @title Swagger Example API
// @version 1.0
// @description This is a sample server celler server.
// @termsOfService http://swagger.io/terms/

// @contact.name API Support
// @contact.url http://www.swagger.io/support
// @contact.email support@swagger.io

// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html

// @host localhost:8080
// @BasePath /api/v1

// @securityDefinitions.basic BasicAuth

// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name Authorization
// @description Description for what is this security definition being used

// @securitydefinitions.oauth2.application OAuth2Application
// @tokenUrl https://example.com/oauth/token
// @scope.write Grants write access
// @scope.admin Grants read and write access to administrative information

// @securitydefinitions.oauth2.implicit OAuth2Implicit
// @authorizationUrl https://example.com/oauth/authorize
// @scope.write Grants write access
// @scope.admin Grants read and write access to administrative information

// @securitydefinitions.oauth2.password OAuth2Password
// @tokenUrl https://example.com/oauth/token
// @scope.read Grants read access
// @scope.write Grants write access
// @scope.admin Grants read and write access to administrative information

// @securitydefinitions.oauth2.accessCode OAuth2AccessCode
// @tokenUrl https://example.com/oauth/token
// @authorizationUrl https://example.com/oauth/authorize
// @scope.admin Grants read and write access to administrative information

func main() {
r := gin.Default()
c := controller.NewController()
v1 := r.Group("/api/v1")
{
admin := v1.Group("/admin") {
admin.Use(auth())
admin.POST("/auth", c.Auth)
}
// 路由分組
examples := v1.Group("/examples"){
examples.GET("ping", c.PingExample)
examples.GET("calc", c.CalcExample)
examples.GET("groups/:group_id/accounts/:account_id", c.PathParamsExample)
examples.GET("header", c.HeaderExample)
examples.GET("securities", c.SecuritiesExample)
examples.GET("attribute", c.AttributeExample)
}
}
// 關鍵點設置 Swagger 路由
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
r.Run(":8080")
}

func auth() gin.HandlerFunc {
return func(c *gin.Context) {
if len(c.GetHeader("Authorization")) == 0 {
httputil.NewError(c, http.StatusUnauthorized, errors.New("Authorization is required Header"))
c.Abort()
}
c.Next()
}
}

此外,一些通用API信息可以動態設置。生成的代碼包文檔導出SwaggerInfo變量,我們可以使用該變量以編程方式設置標題、描述、版本、主機和基本路徑。

package main

import (
"github.com/gin-gonic/gin"
"github.com/swaggo/files"
"github.com/swaggo/gin-swagger"

"./docs" // docs is generated by Swag CLI, you have to import it.
)

// @contact.name API Support
// @contact.url http://www.swagger.io/support
// @contact.email support@swagger.io

// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
func main() {
// 以編程方式設置swagger信息
docs.SwaggerInfo.Title = "Swagger Example API"
docs.SwaggerInfo.Description = "This is a sample server Petstore server."
docs.SwaggerInfo.Version = "1.0"
docs.SwaggerInfo.Host = "petstore.swagger.io"
docs.SwaggerInfo.BasePath = "/v2"
docs.SwaggerInfo.Schemes = []string{"http", "https"}

r := gin.New()

// use ginSwagger middleware to serve the API docs
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))

r.Run()
}

2. 在控制器(路由處理函數)代碼中使用Swagger注釋來定義API文檔, 此處只是展示兩種情況GET或者POST請求, 有興趣的朋友請自行查看文檔。

// 以下是一個使用Swagger注釋的示例:
// GET 請求示例

// @Summary get request example
// @Description get request example
// @Tags example
// @Accept json
// @Produce plain
// @Param val1 query int true "used for calc"
// @Param enumstring query string false "string enums" Enums(A, B, C)
// @Param enumint query int false "int enums" Enums(1, 2, 3)
// @Param enumnumber query number false "int enums" Enums(1.1, 1.2, 1.3)
// @Param string query string false "string valid" minlength(5) maxlength(10)
// @Param int query int false "int valid" minimum(1) maximum(10)
// @Param default query string false "string default" default(A)
// @Param example query string false "string example" example(string)
// @Param collection query []string false "string collection" collectionFormat(multi)
// @Param extensions query []string false "string collection" extensions(x-example=test,x-nullable)
// @Param group_id path int true "Group ID"
// @Param group_id path int true "Group ID"
// @Param Authorization header string true "Authentication header"
// @Success 200 {string} string "answer"
// @Failure 400 {string} string "ok"
// @Failure 404 {string} string "ok"
// @Failure 500 {string} string "ok"
// @Router /examples/groups/{group_id}/accounts/{account_id} [get]
func (c *Controller) AttributeExample(ctx *gin.Context) {
// URL路由參數 /{group_id}
groupID, err := strconv.Atoi(ctx.Param("group_id"))
if err != nil {
httputil.NewError(ctx, http.StatusBadRequest, err)
return
}
accountID, err := strconv.Atoi(ctx.Param("account_id"))
if err != nil {
httputil.NewError(ctx, http.StatusBadRequest, err)
return
}

// Header 參數
auth := ctx.GetHeader("Authorization")

// URL請求參數 ?
ctx.String(http.StatusOK, fmt.Sprintf("enumstring=%s enumint=%s enumnumber=%s string=%s int=%s default=%s group_id=%d account_id=%d Authorization=%s",
ctx.Query("enumstring"),
ctx.Query("enumint"),
ctx.Query("enumnumber"),
ctx.Query("string"),
ctx.Query("int"),
ctx.Query("default"),
groupID,
accountID,
auth,
))
}

// POST 請求示例引入接口安全校驗機制
// models/model.go
package model
type Account struct {
id string form:"id" json:"id" binding:"required" example:"100001" User string form:"user" json:"user" binding:"required" example:"weiyigeek.top" Addr string form:"addr" json:"addr" example:"重慶市南岸區學府大道" Hobby []string form:"hobby" json:"hobby" example:"計算機,烹飪,運動" age int form:"age" json:"age" example:"25" } // @Summary post request example // @Description post request example // @Tags example // @Accept json // @Produce plain // @Param message body model.Account true "Account Info" // @Success 200 {string} string "success" // @Failure 500 {string} string "fail" // @Security ApiKeyAuth // @Security OAuth2Implicit[admin, write] // @Router /examples/post [post] func (c *Controller) PostExample(ctx *gin.Context) { ctx.JSON(http.StatusOK, gin.H { "post": "ping", }) }

3. 使用?swag init?命令生成 Swaager UI 所需的?swagger.json?與?swagger.yaml?.

# Powershell
> &"$env:GOPATH/bin/swag" init -g .\main.go
2023/06/13 10:40:11 Generate swagger docs....
2023/06/13 10:40:11 Generate general API Info, search dir:./
.....
2023/06/13 10:40:11 create docs.go at docs/docs.go
2023/06/13 10:40:11 create swagger.json at docs/swagger.json
2023/06/13 10:40:11 create swagger.yaml at docs/swagger.yaml

4. 執行"go run ./main.go"命令運行此Go gin項目,并使用瀏覽器瀏覽到?http://localhost:8080/swagger/index.html?站點, 您將看到Swagger 2.0 Api文檔

示例代碼: https://github.com/swaggo/swag/tree/master/example/celler

4. swag 項目聲明性注釋格式

1) 通用聲明注釋

annotationdescriptionexample
titleRequired. The title of the application.// @title Swagger Example API
versionRequired. Provides the version of the application API.// @version 1.0
descriptionA short description of the application.// @description This is a sample server celler server.
tag.nameName of a tag.// @tag.name This is the name of the tag
tag.descriptionDescription of the tag// @tag.description Cool Description
tag.docs.urlUrl of the external Documentation of the tag// @tag.docs.url https://example.com
tag.docs.descriptionDescription of the external Documentation of the tag// @tag.docs.description Best example documentation
termsOfServiceThe Terms of Service for the API.// @termsOfService http://swagger.io/terms/
contact.nameThe contact information for the exposed API.// @contact.name API Support
contact.urlThe URL pointing to the contact information. MUST be in the format of a URL.// @contact.url http://www.swagger.io/support
contact.emailThe email address of the contact person/organization. MUST be in the format of an email address.// @contact.email support@swagger.io
license.nameRequired. The license name used for the API.// @license.name Apache 2.0
license.urlA URL to the license used for the API. MUST be in the format of a URL.// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
hostThe host (name or ip) serving the API.// @host localhost:8080
BasePathThe base path on which the API is served.// @BasePath /api/v1
acceptA list of MIME types the APIs can consume. Note that Accept only affects operations with a request body, such as POST, PUT and PATCH. Value MUST be as described under Mime Types.// @accept json
produceA list of MIME types the APIs can produce. Value MUST be as described under Mime Types.// @produce json
query.collection.formatThe default collection(array) param format in query,enums:csv,multi,pipes,tsv,ssv. If not set, csv is the default.// @query.collection.format multi
schemesThe transfer protocol for the operation that separated by spaces.// @schemes http https
externalDocs.descriptionDescription of the external document.// @externalDocs.description OpenAPI
externalDocs.urlURL of the external document.// @externalDocs.url https://swagger.io/resources/open-api/
x-nameThe extension key, must be start by x- and take only json value// @x-example-key {“key”: “value”}

2) 接口操作注釋

annotationdescription
descriptionA verbose explanation of the operation behavior.
description.markdownA short description of the application. The description will be read from a file. E.g. @description.markdown details will load details.md
idA unique string used to identify the operation. Must be unique among all API operations.
tagsA list of tags to each API operation that separated by commas.
summaryA short summary of what the operation does.
acceptA list of MIME types the APIs can consume. Note that Accept only affects operations with a request body, such as POST, PUT and PATCH. Value MUST be as described under Mime Types.
produceA list of MIME types the APIs can produce. Value MUST be as described under Mime Types.
paramParameters that separated by spaces. param name,param type,data type,is mandatory?,comment attribute(optional)
securitySecurity to each API operation.
successSuccess response that separated by spaces. return code or default,{param type},data type,comment
failureFailure response that separated by spaces. return code or default,{param type},data type,comment
responseAs same as success and failure
headerHeader in response that separated by spaces. return code,{param type},data type,comment
routerPath definition that separated by spaces. path,[httpMethod]
x-nameThe extension key, must be start by x- and take only json value.
x-codeSampleOptional Markdown usage. take file as parameter. This will then search for a file named like the summary in the given folder.
deprecatedMark endpoint as deprecated.

3) 接口安全認證注釋

annotationdescriptionparametersexample
securitydefinitions.basicBasic auth.// @securityDefinitions.basic BasicAuth
securitydefinitions.apikeyAPI key auth.in, name, description// @securityDefinitions.apikey ApiKeyAuth
securitydefinitions.oauth2.applicationOAuth2 application auth.tokenUrl, scope, description// @securitydefinitions.oauth2.application OAuth2Application
securitydefinitions.oauth2.implicitOAuth2 implicit auth.authorizationUrl, scope, description// @securitydefinitions.oauth2.implicit OAuth2Implicit
securitydefinitions.oauth2.passwordOAuth2 password auth.tokenUrl, scope, description// @securitydefinitions.oauth2.password OAuth2Password
securitydefinitions.oauth2.accessCodeOAuth2 access code auth.tokenUrl, authorizationUrl, scope, description// @securitydefinitions.oauth2.accessCode OAuth2AccessCode
parameters annotationexample
in// @in header
name// @name Authorization
tokenUrl// @tokenUrl https://example.com/oauth/token
authorizationurl// @authorizationurl https://example.com/oauth/authorize
scope.hoge// @scope.write Grants write access
description// @description OAuth protects our entity endpoints

支持的 Mime 類型 / 參數(Param) 類型 / 數據(Data)類型

溫馨提示: swag接受所有格式正確的MIME類型,即match \*/\*,除此之外swag還接受一些MIME類型的別名,如下所示:

最新項目參考地址,由于時間推移(2023年6月13日 16:14:45)建議大家訪問查看最新注釋聲明:

https://github.com/swaggo/swag/blob/master/README.md#declarative-comments-format

5. 實踐內部Go-Gin項目接入Swagger

描述: 此處是作者正在開發的一個內部項目,此處我為其添加上了swagger接口文檔,方便后續內部同事調用。

package main

// @title devopsapi
// @version v1.1
// @description 安全運維中心API接口
// @termsOfService https://weiyigeek.top/terms

// @contact.name 開發者【WeiyiGeek】
// @contact.url http://weiyigeek.top/support
// @contact.email master@weiyigeek.top

// @externalDocs.description DevOpsApi 外部文檔的描述
// @externalDocs.url https://swagger.io/resources/open-api/

// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html

// @host 127.0.0.1:8080
// @BasePath /api/v1

// @schemes http https

// @securityDefinitions.basic BasicAuth
// $ echo -n weiyigeek:123456 | base64
// d2VpeWlnZWVrOjEyMzQ1Ng==

// @securityDefinitions.apikey API鑒權
// @in header
// @name Authorization
// @description Description for what is this security definition being used
func main() {
// 指定 gin 運行模式
gin.SetMode(global.App.Mode)

// 返回一個新的空白Engine實例
r := gin.New()

// 設置日志中間件
r.Use(middleware.Logger())

// 加載自定義路由
router.Load(r)

// 使用 graceful 管理 Gin 服務從而優雅的停止
srv := &graceful.Server{
Timeout: 10 * time.Second,
Server: &http.Server{
// Gin運行的監聽端口
Addr: fmt.Sprintf("%s:%d", global.App.Host, global.App.Port),
// 要調用的處理程序,http.DefaultServeMux如果為nil
Handler: r,
// MaxHeaderBytes控制服務器解析請求標頭的鍵和值(包括請求行)時讀取的最大字節數 (通常情況下不進行設置)
MaxHeaderBytes: 1 << 20,
},
}

// 開啟一個goroutine啟動服務 啟動 HTTP Server
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()

// 在一個新的goroutine中調用給定的函數
// 等待中斷信號
quit := make(chan os.Signal)
// kill 默認會發送 syscall.SIGTERM 信號
// kill -2 發送 syscall.SIGINT 信號,我們常用的Ctrl+C就是觸發系統SIGINT信號
// kill -9 發送 syscall.SIGKILL 信號,但是不能被捕獲,所以不需要添加它
// signal.Notify把收到的 syscall.SIGINT或syscall.SIGTERM 信號轉發給quit
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) // 此處不會阻塞
<-quit // 阻塞在此,當接收到上述兩種信號時才會往下執行
log.Println("Shutdown Server ...")

// 創建一個 5 秒的超時上下文
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

// 關閉 HTTP Server
// // 5秒內優雅關閉服務(將未處理完的請求處理完再關閉服務),超過5秒就超時退出
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("Server Shutdown:", err)
}
log.Println("Server exiting")
}

Step 2.Go-Gin項目routers\router.go文件示例。

package routers

// router 目錄負責路由相關內容,如定義路由及路由處理函數,路由加載的中間件等

import (
"devopsapi/docs"
"devopsapi/handler/app"
"devopsapi/handler/webhook"
send "devopsapi/pkg/common"

"github.com/gin-gonic/gin"
swaggerfiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)

// Load 中負責加載中間件 middlewares、路由 routes 和 handlers
func Load(r *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {

// 請求異常處理
r.NoRoute(send.HandleNotFound)
r.NoMethod(send.HandleNotFound)

// 前端項目靜態資源
r.StaticFile("/", "./static/dist/index.html")
r.StaticFile("/favicon.ico", "./static/dist/favicon.ico")
r.Static("/assets", "./static/dist/assets")

// 單路由
// r.GET("/app/version", app.Version)

// Swagger 對象方式設置基礎路徑
docs.SwaggerInfo.BasePath = "/api/v1"

// 路由組
_app := r.Group("/app")
{
_app.GET("/version", app.Version)
_app.POST("/restart", app.Restart)
}

_v1 := r.Group("/api/v1")
{
_v1.POST("/webhook/qywx", webhook.QYWXSendMsg)
_v1.POST("/webhook/dingtalk", webhook.DingtalkSendMsg)
_v1.POST("/webhook/email", webhook.EmailSendMsg)
}

// ginSwagger 路由處理
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
// 加載中間件
r.Use(mw...)
return r
}

Step 3.Go-Gin項目handler\webhook\qywx.go文件示例,添加了swagger的接口注釋。

package webhook

import (
send "devopsapi/pkg/common"
"devopsapi/pkg/global"
"devopsapi/pkg/util/webhooks"
"fmt"
"log"

"github.com/gin-gonic/gin"
)

type qywxCommonType struct {
Key string form:"key" json:"key" binding:"required" Type string form:"type" json:"type" binding:"required" Msg string form:"msg" json:"msg" binding:"required" User string form:"user" json:"user" } type qywxCustomMarkdownType struct { Key string form:"key" json:"key" binding:"required" Type string form:"type" json:"type" binding:"required" Title string form:"title" json:"title" binding:"required" Project string form:"project" json:"project" binding:"required" Msg string form:"msg" json:"msg" binding:"required" UrlText string form:"urltext" json:"urltext" UrlAddr string form:"urladdr" json:"urladdr" User string form:"user" json:"user" } type qywxImageType struct { Key string form:"key" json:"key" binding:"required" Type string form:"type" json:"type" binding:"required" ImgData string form:"imgdata" json:"imgdata" binding:"required" ImgMd5 string form:"imgmd5" json:"imgmd5" binding:"required" } // 使用企業微信機器人發送信息 (GET)示例 Swagger // @Summary 企業微信機器人信息發送 // @Description 使用企業微信機器人發送多種類型信息到對應群聊 // @Tags 消息通知 // @Accept application/json // @Produce application/json // @Param key query string true "鑒權Key" // @Param type query string true "消息類型" // @Param msg query string true "發送的消息" // @Param user query string false "接收者手機或名稱" // @Param title query string false "消息標題" // @Param project query string false "消息主題" // @Param urltext query string false "消息連接文本" // @Param urladdr query string false "消息連接" // @Param imgdata query string false "圖片base64數據" // @Param imgmd5 query string false "圖片MD5值" // @Success 200 {string} QYWXSendMsg // @Router /webhook/qywx [POST] func QYWXSendMsg(c *gin.Context) { qywx_key := c.Query("key") qywx_url := fmt.Sprintf("%s%s", global.WebHook.Qywechat, qywx_key) msg_type := c.Query("type") ....... 此處略過n行代碼 }

Step 4.Go-Gin項目handler\webhook\email.go文件示例,添加了swagger的接口注釋。

type emailType struct {
Type string form:"type" json:"type" binding:"required" example:"text|html|file|tpl" To []string form:"to" json:"to" binding:"required" example:"master@weiyigeek.top" Cc []string form:"cc" json:"cc" example:"master@weiyigeek.top" Subject string form:"subject" json:"subject" binding:"required" example:"郵件主題" Body string form:"body" json:"body" binding:"required" example:"郵件內容" File string form:"file" json:"file" example:"/app/storage/public/weiyigeek.jpg" Filename string form:"filename" json:"filename" example:"weiyigeek-作者照片" Template string form:"tpl" json:"tpl" example:"TemplateVerifiy" } // 使用企業郵箱發送郵件信息 (POST) 示例 Swagger // @Summary 企業郵箱信息發送 // @Description 使用企業郵箱發送多種類型的郵件信息 // @Tags 消息通知 // @Accept json // @Produce json // @Param type query string true "發送類型" // @Param body body emailType true "請求body" // @Success 200 {string} EmailSendMsg // @Security BasicAuth // @Security API鑒權 // @Router /webhook/email [POST] func EmailSendMsg(c *gin.Context) { sendType := c.Query("type") ....... 此處略過n行代碼 }

Step 5.命令行進入到Go-Gin項目devopsapi目錄中,執行?swag init?生成 swagger-ui 所需文件, 生成完畢后便可運行該項目。

# PowerShell
cd devopsapi # 進入項目根路徑
&"$env:GOPATH/bin/swag" init -g ./main.go -o ./docs # 初始化文檔
# 2023/06/13 17:55:52 Generate swagger docs....
# 2023/06/13 17:55:52 Generate general API Info, search dir:./
# 2023/06/13 17:55:52 Generating webhook.emailType
# 2023/06/13 17:55:52 create docs.go at docs/docs.go
# 2023/06/13 17:55:52 create swagger.json at docs/swagger.json
# 2023/06/13 17:55:52 create swagger.yaml at docs/swagger.yaml

# 運行此app應用
PS D:\Study\Project\Go\devopsapi> go run .\main.go

Step 6.運行后訪問 http://127.0.0.1:8080/swagger/index.html 查看swagger接口文檔,效果如下所示,

除此之外,我們還可以使用swagger-UI進行在線模擬請求我們開發的接口,是不是非常方便呀!

本文至此完畢,更多技術文章,盡情等待下篇好文!

原文地址: https://blog.weiyigeek.top/2023/6-5-746.html

本文章轉載微信公眾號@全棧工程師修煉指南

上一篇:

結合gin+gorm+go-Redis寫一個基礎 API

下一篇:

ASP.NET Core Web API基于RESTFul APIs的集合結果過濾和分頁
#你可能也喜歡這些API文章!

我們有何不同?

API服務商零注冊

多API并行試用

數據驅動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內容創意新穎性、情感共鳴力、商業轉化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費