4.5 KiB
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
標記,但沒有檢查:
- 字段類型是否為指針(指針類型在 Go 中表示可選)
- 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 設計中,以下三種方式都表示字段為可選:
- 指針類型 -
*string
,*int
,*int64
等 - JSON 標籤的
omitempty
-json:"field,omitempty"
- 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 文檔準確性)