Merge pull request '支持免费版redoc swagger文档渲染' (#10) from feature/support_free_redoc into master

Reviewed-on: #10
This commit is contained in:
白茶清欢 2025-02-18 17:07:36 +08:00
commit b8a7f0585a
4 changed files with 72 additions and 10 deletions

View File

@ -11,4 +11,5 @@ const (
SwaggerUIThemeDefault = "swaggerUI" // 文档默认主题
SwaggerUIThemeKnife4go = "knife4go" // knife4go 主题
SwaggerUIThemeYDocLucky = "ydoc-lucky-ui" // YDoc Lucky UI 主题: https://github.com/NoBugBoy/LuckyUI
SwaggerUIThemeRedocFree = "redoc-free" // redoc UI 主题, 开源免费版: https://github.com/Redocly/redoc
)

View File

@ -329,6 +329,7 @@ func (g *Generate) ParseReadConfigParam(requestCfg *define.UriBaseConfig, baseRe
if inputType.Field(i).Anonymous {
// 匿名字段, 直接对齐到当前的父级
g.ParseReadConfigParam(requestCfg, baseReqCfg, inputType.Field(i).Type)
continue
}
propertyName := ParseStructFieldTag.GetParamName(inputType.Field(i))
if propertyName == "-" {
@ -496,15 +497,16 @@ func (g *Generate) AddComponentsSchema(rootSchemaName string, pkgPath string, in
// 结构体
if inputType.Kind() == reflect.Struct {
for i := 0; i < inputType.NumField(); i++ {
propertyName := ParseStructFieldTag.GetParamName(inputType.Field(i))
if propertyName == "-" {
continue
}
if inputType.Field(i).Anonymous {
// 处理匿名字段
g.handleAnonymousField(schemaName, inputType.Field(i))
continue
}
propertyName := ParseStructFieldTag.GetParamName(inputType.Field(i))
if propertyName == "-" {
continue
}
if inputType.Field(i).Type.Kind() == reflect.Ptr {
// 处理指针
if inputType.Field(i).Type.Elem().Kind() == reflect.Struct {
@ -605,15 +607,15 @@ func (g *Generate) handleAnonymousField(schemaName string, field reflect.StructF
handleType = handleType.Elem()
}
for i := 0; i < handleType.NumField(); i++ {
if handleType.Field(i).Anonymous {
itemField := handleType.Field(i)
if itemField.Anonymous {
// 递归处理多层嵌套匿名字段
g.handleAnonymousField(schemaName, handleType.Field(i))
g.handleAnonymousField(schemaName, itemField)
continue
} else {
itemField := handleType.Field(i)
baseConvertType, isBaseType := g.realBaseType2SwaggerType(itemField.Type.String())
if !isBaseType {
g.AddComponentsSchema(schemaName, itemField.Type.PkgPath(), itemField.Type)
g.AddComponentsSchema(schemaName, handleType.Field(i).Type.PkgPath(), handleType.Field(i).Type)
continue
} else {
paramName := ParseStructFieldTag.GetParamName(itemField)
@ -621,8 +623,8 @@ func (g *Generate) handleAnonymousField(schemaName string, field reflect.StructF
Type: baseConvertType,
Format: itemField.Type.String(),
Enum: ValidateRule.Enum(itemField),
Default: ParseStructFieldTag.GetDefaultValue(itemField),
Description: ParseStructFieldTag.GetParamDesc(itemField),
Default: ParseStructFieldTag.GetDefaultValue(handleType.Field(i)),
Description: ParseStructFieldTag.GetParamDesc(handleType.Field(i)),
}
if ValidateRule.IsRequired(itemField) {
g.docData.Components.Schemas[schemaName].Required = append(g.docData.Components.Schemas[schemaName].Required, paramName)

29
redoc-free/index.html Normal file
View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html>
<head>
<title>Redoc</title>
<!-- needed for adaptive design -->
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700 -->
<!-- 预留占位符, 可以通过外部替换 -->
<link href="{{CSS_FAMILY}}" rel="stylesheet">
<!--
Redoc doesn't change outer page styles
-->
<style>
body {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<!-- 预留占位符, 可以通过外部替换 -->
<!--doc.json, 相对于外部设置好的base_url-->
<redoc spec-url='{{DOC_PATH}}'></redoc>
<!--https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js-->
<script src="{{REDOC_STANDALONE_JS}}"> </script>
</body>
</html>

View File

@ -23,6 +23,9 @@ import (
//go:embed ydoc-lucky-ui/*
var ydocUIFiles embed.FS
//go:embed redoc-free/index.html
var redocFreeIndexContent string
// NewSwaggerUI ...
//
// Author : go_developer@163.com<白茶清欢>
@ -106,6 +109,11 @@ func (su *SwaggerUI) Handler() func(ctx *gin.Context) {
case define.SwaggerUIThemeYDocLucky:
// YDoc-Lucky-UI 主题处理
return su.HandleLuckyUI()
case define.SwaggerUIThemeDefault:
return su.HandleSwaggerUI()
case define.SwaggerUIThemeRedocFree:
// redoc免费版
return su.HandleRedocFreeUI()
default:
return su.HandleSwaggerUI()
}
@ -160,3 +168,25 @@ func (su *SwaggerUI) HandleKnife4goUI() func(ctx *gin.Context) {
func (su *SwaggerUI) HandleSwaggerUI() func(ctx *gin.Context) {
return ginSwagger.WrapHandler(swaggerFiles.Handler)
}
// HandleRedocFreeUI 处理redoc_free主题
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:40 2025/2/18
func (su *SwaggerUI) HandleRedocFreeUI() func(ctx *gin.Context) {
return func(ctx *gin.Context) {
// TODO : 这部分数据支持外部传参替换
replaceTable := map[string]string{
"{{CSS_FAMILY}}": "https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700",
"{{DOC_PATH}}": "doc.json",
"{{REDOC_STANDALONE_JS}}": "https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js",
}
for k, v := range replaceTable {
redocFreeIndexContent = strings.ReplaceAll(redocFreeIndexContent, k, v)
}
ctx.Header("Content-Type", "text/html; charset=utf-8")
ctx.String(http.StatusOK, redocFreeIndexContent)
ctx.Abort()
}
}