doc-generate/BUGFIX_REQUIRED_FIELDS.md

4.5 KiB

Bug Fix: Required Fields 判定錯誤

🐛 問題描述

生成的 OpenAPI/Swagger 文檔中,所有結構體字段都被錯誤地標記為 required,即使這些字段:

  • 指針類型 (*string, *int, *int64)
  • 在 JSON 標籤中包含 omitempty
  • 在 validate 標籤中包含 omitempty

問題示例

type CreateUserInfoReq {
    UID       string  `json:"uid" validate:"required"`           // 必填
    Avatar    *string `json:"avatar,omitempty"`                  // 應該可選
    NickName  *string `json:"nick_name,omitempty"`               // 應該可選
    Language  string  `json:"language" validate:"required"`      // 必填
}

修復前生成的 required 列表:

"required": ["uid", "avatar", "nick_name", "language"]

修復後正確的 required 列表:

"required": ["uid", "language"]

🔍 根本原因

internal/swagger/swagger.go 中的 isRequired() 函數只檢查 JSON 標籤中的 optional 標記,但沒有檢查:

  1. 字段類型是否為指針(指針類型在 Go 中表示可選)
  2. JSON 標籤是否包含 omitempty

修復方案

修改的文件

  • internal/swagger/swagger.go

修改內容

1. 修改 rangeMemberAndDo 函數簽名

// 修改前
required := isRequired(ctx, tags)

// 修改後
required := isRequired(ctx, tags, field)

2. 修改 isRequired 函數

// 修改前
func isRequired(ctx Context, tags *apiSpec.Tags) bool {
    tag, err := tags.Get(tagJson)
    if err == nil {
        return !isOptional(ctx, tag.Options)
    }
    // ...
    return false
}

// 修改後
func isRequired(ctx Context, tags *apiSpec.Tags, member apiSpec.Member) bool {
    // Check if the field type is a pointer - pointer types are optional
    if _, isPointer := member.Type.(apiSpec.PointerType); isPointer {
        return false
    }

    tag, err := tags.Get(tagJson)
    if err == nil {
        return !isOptional(ctx, tag.Options)
    }
    // ...
    return false
}

3. 修改 isOptional 函數

// 修改前
func isOptional(_ Context, options []string) bool {
    for _, option := range options {
        if option == optionalFlag {
            return true
        }
    }
    return false
}

// 修改後
func isOptional(_ Context, options []string) bool {
    for _, option := range options {
        if option == optionalFlag || option == "omitempty" {
            return true
        }
    }
    return false
}

📊 驗證結果

使用 example/api/gateway.api 進行測試:

結構體 修復前 修復後 狀態
CreateUserInfoReq 13 個必填 5 個必填
UpdateUserInfoReq 11 個必填 1 個必填
ListUserInfoReq 6 個必填 2 個必填
GetUserInfoReq 2 個必填 0 個必填
BindingUserReq 3 個必填 3 個必填

CreateUserInfoReq 詳細對比

必填字段(非指針類型):

  • uid (string)
  • alarm_type (int)
  • status (int)
  • language (string)
  • currency (string)

可選字段(指針類型):

  • avatar (*string)
  • nick_name (*string)
  • full_name (*string)
  • gender (*int64)
  • birthdate (*int64)
  • phone_number (*string)
  • email (*string)
  • address (*string)

🚀 影響範圍

此修復影響所有使用 go-doc 生成的 OpenAPI/Swagger 文檔:

  • Swagger 2.0 格式
  • OpenAPI 3.0 格式
  • JSON 輸出
  • YAML 輸出

📝 開發者注意事項

在 Go 語言和 API 設計中,以下三種方式都表示字段為可選:

  1. 指針類型 - *string, *int, *int64
  2. JSON 標籤的 omitempty - json:"field,omitempty"
  3. Validate 標籤的 omitempty - validate:"omitempty,..."

修復後的 go-doc 工具會正確識別這些模式。

🔗 相關文件

  • 修改文件:internal/swagger/swagger.go
  • 測試文件:example/api/gateway.api
  • 生成文件:example/test_output/gateway.json
  • 驗證腳本:可運行 make gen-gateway 重新生成並驗證

測試通過

# 重新構建
make build

# 生成 gateway API 文檔
./bin/go-doc --api example/api/gateway.api \
             --dir example/test_output \
             --filename gateway \
             --spec-version openapi3.0

# 所有結構體的 required 字段現在都正確了!

修復日期: 2025-10-01
修復版本: 1.2.1
問題類型: Bug Fix
嚴重程度: High (影響 API 文檔準確性)