支持免费版redoc swagger文档渲染 #10
@ -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
|
||||
)
|
||||
|
22
generate.go
22
generate.go
@ -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
29
redoc-free/index.html
Normal 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>
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user