# go-doc [![Go Version](https://img.shields.io/badge/Go-%3E%3D%201.23-blue)](https://go.dev/) [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) [![Version](https://img.shields.io/badge/version-1.2.0-blue)](CHANGELOG.md) **go-doc** 是一個獨立的命令行工具,專門用於將 [go-zero](https://github.com/zeromicro/go-zero) `.api` 文件轉換為 OpenAPI 規範文檔。 原本是 go-zero 專案的一部分,現在已獨立出來,成為一個易於使用的工具,支援生成 **Swagger 2.0** 和 **OpenAPI 3.0** 兩種格式。 --- ## 📑 目錄 - [✨ 特性](#-特性) - [📦 安裝](#-安裝) - [🚀 快速開始](#-快速開始) - [📖 API 文件格式](#-api-文件格式) - [🆕 @respdoc 多狀態碼回應](#-respdoc-多狀態碼回應) - [🔧 進階功能](#-進階功能) - [🎯 OpenAPI 3.0 vs Swagger 2.0](#-openapi-30-vs-swagger-20) - [💡 使用範例](#-使用範例) - [🧪 測試](#-測試) - [📚 專案結構](#-專案結構) - [🔄 從 go-zero 遷移](#-從-go-zero-遷移) - [📝 版本記錄](#-版本記錄) - [❓ 常見問題](#-常見問題) - [🤝 貢獻](#-貢獻) - [📄 授權](#-授權) --- ## ✨ 特性 - 🚀 **獨立二進制** - 無需依賴 go-zero 運行時 - 📝 **雙規格支援** - 生成 **Swagger 2.0** 和 **OpenAPI 3.0** 格式 - 🎯 **豐富的類型支援** - 處理結構體、陣列、映射、指針和嵌套類型 - 🏷️ **基於標籤的配置** - 支援 `json`、`form`、`path`、`header` 標籤 - 📊 **進階驗證** - 範圍、枚舉、預設值、範例值 - 🔐 **安全定義** - 自定義身份驗證配置 - 📦 **多種輸出格式** - JSON 或 YAML 輸出 - 🎨 **定義引用** - 可選使用 definitions/schemas 使輸出更簡潔 - 🔄 **自動轉換** - Swagger 2.0 到 OpenAPI 3.0 的無縫轉換 - **🆕 多狀態碼回應** - 使用 `@respdoc` 定義多個 HTTP 狀態碼 - **🆕 業務錯誤碼映射** - 將業務錯誤碼映射到不同的錯誤類型,OpenAPI 3.0 使用 `oneOf` --- ## 📦 安裝 ### 從源碼編譯 ```bash git clone cd go-doc make build ``` 編譯完成後,執行檔位於 `bin/go-doc` ### 使用 Go Install ```bash go install github.com/danielchan-25/go-doc/cmd/go-doc@latest ``` --- ## 🚀 快速開始 ### 基本使用 ```bash # 生成 Swagger 2.0(預設) go-doc -a example/example.api -d output # 生成 OpenAPI 3.0(推薦) go-doc -a example/example.api -d output -s openapi3.0 # 生成 YAML 格式 go-doc -a example/example.api -d output -y # 生成 OpenAPI 3.0 YAML go-doc -a example/example.api -d output -s openapi3.0 -y # 自訂檔名 go-doc -a example/example.api -d output -f my-api ``` ### 命令行選項 ``` Flags: -a, --api string API 文件路徑(必要) -d, --dir string 輸出目錄(必要) -f, --filename string 輸出檔名(不含副檔名) -h, --help 顯示說明 -s, --spec-version string OpenAPI 規格版本:swagger2.0 或 openapi3.0(預設:swagger2.0) -v, --version 顯示版本 -y, --yaml 生成 YAML 格式(預設:JSON) ``` ### 使用 Makefile ```bash # 編譯 make build # 生成範例 make example # 清理 make clean # 運行測試 make test # 查看所有命令 make help ``` --- ## 📖 API 文件格式 ### 基本結構 ```go syntax = "v1" info ( title: "My API" description: "API documentation" version: "v1.0.0" host: "api.example.com" basePath: "/v1" ) type ( UserRequest { Id int `json:"id,range=[1:10000]"` Name string `json:"name"` } UserResponse { Id int `json:"id"` Name string `json:"name"` } ) @server ( tags: "user" ) service MyAPI { @handler getUser get /user/:id (UserRequest) returns (UserResponse) } ``` ### 支援的 Info 屬性 - `title` - API 標題 - `description` - API 描述 - `version` - API 版本 - `host` - API 主機(如 "api.example.com") - `basePath` - 基礎路徑(如 "/v1") - `schemes` - 協議(如 "http,https") - `consumes` - 請求內容類型 - `produces` - 回應內容類型 - `contactName`, `contactURL`, `contactEmail` - 聯絡資訊 - `licenseName`, `licenseURL` - 授權資訊 - `useDefinitions` - 使用 Swagger definitions(true/false) - `wrapCodeMsg` - 將回應包裝在 `{code, msg, data}` 結構中 - `securityDefinitionsFromJson` - JSON 格式的安全定義 ### 標籤選項 #### JSON 標籤 ```go type Example { // 範圍驗證 Age int `json:"age,range=[1:150]"` // 預設值 Status string `json:"status,default=active"` // 範例值 Email string `json:"email,example=user@example.com"` // 枚舉值 Role string `json:"role,options=admin|user|guest"` // 可選欄位 Phone string `json:"phone,optional"` } ``` #### Form 標籤 ```go type QueryRequest { Keyword string `form:"keyword"` Page int `form:"page,default=1"` Size int `form:"size,range=[1:100]"` } ``` #### Path 標籤 ```go type PathRequest { UserId int `path:"userId,range=[1:999999]"` } ``` #### Header 標籤 ```go type HeaderRequest { Token string `header:"Authorization"` } ``` --- ## 🆕 @respdoc 多狀態碼回應 ### 概述 `@respdoc` 註解允許您為單個 API 端點定義多個 HTTP 狀態碼的回應,並支援為同一狀態碼定義多種業務錯誤格式。 **重要特性:** - ✅ 支援多個 HTTP 狀態碼(200, 201, 400, 401, 404, 500 等) - ✅ 支援業務錯誤碼映射(如 300101, 300102 等) - ✅ **OpenAPI 3.0 使用 `oneOf` 表示多種錯誤格式** - ✅ Swagger 2.0 在描述中列出所有可能的錯誤 ### 基本語法 #### 單一回應類型 ```go service MyAPI { @doc ( description: "用戶查詢接口" ) /* @respdoc-200 (UserResponse) // 成功 @respdoc-400 (ErrorResponse) // 錯誤請求 @respdoc-401 (UnauthorizedError) // 未授權 @respdoc-500 (ServerError) // 服務器錯誤 */ @handler getUser get /user/:id (UserRequest) returns (UserResponse) } ``` #### 多業務錯誤碼(重點功能)⭐ ```go service MyAPI { @doc ( description: "創建訂單" ) /* @respdoc-201 (OrderResponse) // 創建成功 @respdoc-400 ( 300101: (ValidationError) 參數驗證失敗 300102: (InsufficientStockError) 庫存不足 300103: (InvalidPaymentError) 支付方式無效 ) // 客戶端錯誤 @respdoc-401 (UnauthorizedError) // 未授權 @respdoc-500 (ServerError) // 服務器錯誤 */ @handler createOrder post /order (OrderRequest) returns (OrderResponse) } ``` ### 完整範例 ```go syntax = "v1" info ( title: "訂單 API" description: "訂單管理系統" version: "v1" host: "api.example.com" basePath: "/v1" useDefinitions: true ) type ( CreateOrderReq { ProductID string `json:"product_id"` Quantity int `json:"quantity"` PaymentMethod string `json:"payment_method"` } OrderResponse { OrderID string `json:"order_id"` Status string `json:"status"` Total float64 `json:"total"` } ValidationError { Code string `json:"code"` Message string `json:"message"` Fields []string `json:"fields"` } InsufficientStockError { Code string `json:"code"` Message string `json:"message"` ProductID string `json:"product_id"` Required int `json:"required"` Available int `json:"available"` } InvalidPaymentError { Code string `json:"code"` Message string `json:"message"` Method string `json:"method"` } UnauthorizedError { Code string `json:"code"` Message string `json:"message"` Reason string `json:"reason"` } ServerError { Code string `json:"code"` Message string `json:"message"` TraceID string `json:"trace_id"` } ) @server ( tags: "order" ) service OrderAPI { @doc ( description: "創建訂單" ) /* @respdoc-201 (OrderResponse) // 創建成功 @respdoc-400 ( 300101: (ValidationError) 參數驗證失敗 300102: (InsufficientStockError) 庫存不足 300103: (InvalidPaymentError) 支付方式無效 ) // 客戶端錯誤 @respdoc-401 (UnauthorizedError) // 未授權 @respdoc-500 (ServerError) // 服務器錯誤 */ @handler createOrder post /order (CreateOrderReq) returns (OrderResponse) } ``` ### 生成結果對比 #### Swagger 2.0 輸出 ```json { "paths": { "/v1/order": { "post": { "responses": { "201": { "description": "// 創建成功", "schema": { "$ref": "#/definitions/OrderResponse" } }, "400": { "description": "客戶端錯誤\n\nPossible errors:\n300101: ValidationError - 參數驗證失敗\n300102: InsufficientStockError - 庫存不足\n300103: InvalidPaymentError - 支付方式無效", "schema": { "$ref": "#/definitions/ValidationError" } } } } } } } ``` #### OpenAPI 3.0 輸出(使用 oneOf)⭐ ```json { "paths": { "/v1/order": { "post": { "responses": { "201": { "description": "// 創建成功", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OrderResponse" } } } }, "400": { "description": "客戶端錯誤", "content": { "application/json": { "schema": { "oneOf": [ { "$ref": "#/components/schemas/ValidationError" }, { "$ref": "#/components/schemas/InsufficientStockError" }, { "$ref": "#/components/schemas/InvalidPaymentError" } ] } } } } } } } } } ``` **優勢:** OpenAPI 3.0 使用 `oneOf` 精確表示多種可能的錯誤類型! --- ## 🔧 進階功能 ### 安全定義 ```go info ( securityDefinitionsFromJson: `{ "apiKey": { "type": "apiKey", "name": "x-api-key", "in": "header", "description": "API Key Authentication" } }` ) @server ( authType: apiKey ) service MyAPI { // 此處的路由將使用 apiKey 身份驗證 } ``` ### Code-Msg 包裝 ```go info ( wrapCodeMsg: true bizCodeEnumDescription: "1001-User not found
1002-Permission denied" ) ``` 回應將被包裝為: ```json { "code": 0, "msg": "ok", "data": { /* your actual response */ } } ``` --- ## 🎯 OpenAPI 3.0 vs Swagger 2.0 ### 規格版本 #### Swagger 2.0(預設) ```bash go-doc -a example/example.api -d output # 或明確指定 go-doc -a example/example.api -d output -s swagger2.0 ``` #### OpenAPI 3.0(推薦) ```bash go-doc -a example/example.api -d output -s openapi3.0 ``` ### 主要差異 | 特性 | Swagger 2.0 | OpenAPI 3.0 | |:---|:---|:---| | **版本聲明** | `swagger: "2.0"` | `openapi: "3.0.3"` | | **服務器** | `host`, `basePath`, `schemes` | `servers` 陣列 | | **Schema 定義** | `definitions` | `components/schemas` | | **安全定義** | `securityDefinitions` | `components/securitySchemes` | | **請求體** | `parameters[in=body]` | `requestBody` | | **多類型回應** | ❌ 不支援 | ✅ `oneOf`/`anyOf`/`allOf` | ### 範例對比 **Swagger 2.0:** ```json { "swagger": "2.0", "host": "example.com", "basePath": "/v1", "definitions": { "User": {...} }, "securityDefinitions": { "apiKey": {...} } } ``` **OpenAPI 3.0:** ```json { "openapi": "3.0.3", "servers": [ {"url": "http://example.com/v1"}, {"url": "https://example.com/v1"} ], "components": { "schemas": { "User": {...} }, "securitySchemes": { "apiKey": {...} } } } ``` --- ## 💡 使用範例 ### 場景 1:RESTful CRUD ```go /* @respdoc-200 (UserResponse) // 獲取成功 @respdoc-404 (NotFoundError) // 用戶不存在 @respdoc-401 (UnauthorizedError) // 未授權 */ @handler getUser get /user/:id (UserIdReq) returns (UserResponse) /* @respdoc-201 (UserResponse) // 創建成功 @respdoc-400 (ValidationError) // 參數錯誤 @respdoc-409 (ConflictError) // 用戶已存在 */ @handler createUser post /user (CreateUserReq) returns (UserResponse) /* @respdoc-200 (UserResponse) // 更新成功 @respdoc-400 (ValidationError) // 參數錯誤 @respdoc-404 (NotFoundError) // 用戶不存在 */ @handler updateUser put /user/:id (UpdateUserReq) returns (UserResponse) /* @respdoc-204 // 刪除成功 @respdoc-404 (NotFoundError) // 用戶不存在 */ @handler deleteUser delete /user/:id (UserIdReq) ``` ### 場景 2:複雜業務流程 ```go /* @respdoc-200 (PaymentResponse) // 支付成功 @respdoc-400 ( 400001: (InsufficientBalanceError) 餘額不足 400002: (InvalidCardError) 無效的卡號 400003: (ExpiredCardError) 卡已過期 400004: (DailyLimitError) 超過每日限額 ) // 支付失敗 @respdoc-401 (UnauthorizedError) // 未授權 @respdoc-422 (ProcessingError) // 處理中請勿重複提交 */ @handler processPayment post /payment (PaymentRequest) returns (PaymentResponse) ``` ### 場景 3:狀態機轉換 ```go /* @respdoc-200 (OrderResponse) // 狀態更新成功 @respdoc-400 ( 400001: (InvalidStateError) 無效的狀態轉換 400002: (OrderCancelledError) 訂單已取消 400003: (OrderCompletedError) 訂單已完成 ) // 狀態轉換失敗 */ @handler updateOrderStatus put /order/:id/status (UpdateStatusReq) returns (OrderResponse) ``` --- ## 🧪 測試 ### 測試所有格式 ```bash # 測試 Swagger 2.0 和 OpenAPI 3.0 ./test_all_formats.sh # 測試 @respdoc 功能 ./test_respdoc.sh ``` ### 手動測試 ```bash # 生成並檢查 go-doc -a example/example_respdoc.api -d output -s openapi3.0 # 查看生成的文檔 cat output/example_respdoc.json | jq '.paths."/v1/order".post.responses' ``` --- ## 📚 專案結構 ``` go-doc/ ├── cmd/ │ └── go-doc/ # 主程式入口 │ └── main.go ├── internal/ │ ├── swagger/ # 核心邏輯 │ │ ├── respdoc.go # @respdoc 解析 │ │ ├── response.go # 回應生成 │ │ ├── openapi3.go # OpenAPI 3.0 轉換 │ │ ├── swagger.go # Swagger 2.0 生成 │ │ ├── path.go # 路徑處理 │ │ ├── parameter.go # 參數處理 │ │ ├── definition.go # 定義處理 │ │ └── ... │ └── util/ # 工具函數 │ ├── util.go │ ├── stringx.go │ └── pathx.go ├── example/ │ ├── example.api # 範例 API │ ├── example_cn.api # 中文範例 │ ├── example_respdoc.api # @respdoc 範例 │ └── test_output/ # 生成的文檔 ├── bin/ # 編譯後的執行檔 ├── go.mod ├── go.sum ├── Makefile ├── README.md ├── LICENSE ├── CHANGELOG.md ├── test_all_formats.sh # 測試腳本 └── test_respdoc.sh # @respdoc 測試 ``` --- ## 🔄 從 go-zero 遷移 ### 主要變更 #### 1. 模組獨立 **之前(go-zero plugin):** ```go // 屬於 go-zero 內部工具 package swagger // in tools/goctl/api/plugin/swagger/ ``` **現在(standalone):** ```go module go-doc package swagger // in internal/swagger/ ``` #### 2. 依賴簡化 | 依賴 | 替換為 | 原因 | |:---|:---|:---| | `go-zero/tools/goctl/internal/version` | 自定義版本 | 內部包不可訪問 | | `go-zero/tools/goctl/util` | `go-doc/internal/util` | 自包含工具 | | `go-zero/tools/goctl/util/stringx` | `go-doc/internal/util` | 自定義字串處理 | | `go-zero/tools/goctl/util/pathx` | `go-doc/internal/util` | 自定義路徑處理 | | `google.golang.org/grpc/metadata` | 原生 map 處理 | 移除外部依賴 | #### 3. 保留的依賴 - ✅ `github.com/go-openapi/spec` - Swagger 2.0 - ✅ `github.com/getkin/kin-openapi` - OpenAPI 3.0 - ✅ `github.com/spf13/cobra` - CLI 框架 - ✅ `github.com/zeromicro/go-zero/tools/goctl/api/spec` - API 解析 - ✅ `gopkg.in/yaml.v2` - YAML 支援 ### 遷移步驟 1. **安裝 go-doc** ```bash go build -o bin/go-doc ./cmd/go-doc ``` 2. **替換命令** **之前:** ```bash goctl api plugin -plugin goctl-swagger="swagger -filename api.json" -api user.api -dir . ``` **現在:** ```bash go-doc -a user.api -d . -f api ``` 3. **新功能** 使用 OpenAPI 3.0: ```bash go-doc -a user.api -d . -f api -s openapi3.0 ``` 使用 @respdoc: ```go /* @respdoc-200 (Response) // 成功 @respdoc-400 (Error) // 錯誤 */ ``` --- ## 📝 版本記錄 ### [1.2.0] - 2025-09-30 #### 新增 - **@respdoc 註解支援** - 為單個端點定義多個 HTTP 狀態碼 - **業務錯誤碼映射** - 將不同的業務錯誤碼映射到特定錯誤類型 - **OpenAPI 3.0 oneOf 支援** - 使用 `oneOf` 表示多種可能的錯誤回應 - 測試腳本 `test_respdoc.sh` - 支援解析 `HandlerDoc` 註釋(在 `@doc()` 外) #### 增強 - 回應生成現在支援多個狀態碼(200, 201, 400, 401, 404, 500 等) - OpenAPI 3.0 生成器為業務錯誤碼創建 `oneOf` schema - Swagger 2.0 在描述中列出所有可能的錯誤 ### [1.1.0] - 2025-09-30 #### 新增 - **OpenAPI 3.0 支援** - 生成 Swagger 2.0 和 OpenAPI 3.0 規格 - 新增 `--spec-version` (`-s`) 標誌 - 自動從 Swagger 2.0 轉換到 OpenAPI 3.0 - 整合 `github.com/getkin/kin-openapi` 函式庫 ### [1.0.0] - 2025-09-30 #### 新增 - 初始發布為獨立工具 - 從 go-zero 專案提取並獨立 - 支援將 go-zero `.api` 文件轉換為 Swagger 2.0 規格 - JSON 和 YAML 輸出格式 - 使用 cobra 的命令行介面 --- ## ❓ 常見問題 ### Q: 應該使用哪個版本? **A:** - **Swagger 2.0**: 如果需要與舊工具或系統相容 - **OpenAPI 3.0**: 推薦使用,功能更強大且為現代標準 ### Q: 為什麼 OpenAPI 3.0 使用 oneOf? **A:** `oneOf` 是 OpenAPI 3.0 的標準方式來表示"多選一"的 schema。這讓 API 文檔更精確,工具(如 Swagger UI、代碼生成器)可以正確理解和處理多種可能的回應類型。 ### Q: Swagger 2.0 為什麼不支援 oneOf? **A:** Swagger 2.0 規範不包含 `oneOf` 關鍵字。我們在描述中列出所有可能的錯誤,並使用第一個類型作為 schema 示例。 ### Q: 可以同時生成兩種格式嗎? **A:** 可以!執行兩次命令即可: ```bash go-doc -a api.api -d out -f api-v2 go-doc -a api.api -d out -f api-v3 -s openapi3.0 ``` ### Q: 可以不定義業務錯誤碼嗎? **A:** 可以!如果只有一種錯誤類型,直接使用單一回應格式: ```go @respdoc-400 (ValidationError) // 參數錯誤 ``` ### Q: 如何驗證生成的文檔? **A:** 可以使用以下工具: - **Swagger Editor**: https://editor.swagger.io/ - **Swagger UI**: 本地運行或線上版本 - **OpenAPI Generator**: 生成客戶端/伺服器代碼來驗證 --- ## 🤝 貢獻 歡迎貢獻!請隨時提交 Pull Request。 ### 開發 ```bash # 克隆專案 git clone cd go-doc # 安裝依賴 go mod download # 運行測試 make test # 編譯 make build # 運行範例 make example ``` --- ## 📄 授權 MIT License Copyright (c) 2025 Daniel Chan 本專案最初是 [go-zero](https://github.com/zeromicro/go-zero) 專案 swagger 生成插件的一部分。我們感謝 go-zero 團隊的出色工作。 --- ## 🎉 總結 **go-doc v1.2.0** 提供了強大的 API 文檔生成功能: ✅ **雙規格支援** - Swagger 2.0 和 OpenAPI 3.0 ✅ **多狀態碼回應** - 使用 @respdoc 定義完整的 HTTP 回應 ✅ **業務錯誤碼映射** - 支援複雜的錯誤場景 ✅ **OpenAPI 3.0 oneOf** - 專業的多類型回應表示 ✅ **完整測試** - 自動化測試腳本 ✅ **詳細文檔** - 完整的使用指南 開始使用: ```bash go-doc -a your-api.api -d docs -s openapi3.0 ``` 查看範例: - 基本範例:`example/example.api` - @respdoc 範例:`example/example_respdoc.api` - 測試腳本:`./test_respdoc.sh` --- **🌟 如果這個專案對您有幫助,請給個 Star!**