This commit is contained in:
ArtoriasWu
2026-06-24 16:22:24 +08:00
parent 60b3afb954
commit 4b7ce6790f
24 changed files with 3421 additions and 0 deletions
@@ -0,0 +1,183 @@
using System.Text.Json;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace MiGu.Server.OpenApi;
/// <summary>
/// 将 SimpleLite EmbedIO WebApi 的 OpenAPI 描述合并进 MiGu.Server Swagger 文档。
/// 源文件:Simple/SimpleLite/Docs/openapi/simplelite-projection.json
/// </summary>
public sealed class SimpleLiteOpenApiDocumentFilter : IDocumentFilter
{
private static readonly Dictionary<string, OperationType> VerbMap = new(StringComparer.OrdinalIgnoreCase)
{
["get"] = OperationType.Get,
["post"] = OperationType.Post,
["put"] = OperationType.Put,
["patch"] = OperationType.Patch,
["delete"] = OperationType.Delete,
["head"] = OperationType.Head,
["options"] = OperationType.Options,
["trace"] = OperationType.Trace
};
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
var json = TryLoadJson();
if (json == null) return;
using var doc = JsonDocument.Parse(json);
var root = doc.RootElement;
if (root.TryGetProperty("tags", out var tagsEl) && tagsEl.ValueKind == JsonValueKind.Array)
{
swaggerDoc.Tags ??= new List<OpenApiTag>();
foreach (var tag in tagsEl.EnumerateArray())
{
if (!tag.TryGetProperty("name", out var nameEl)) continue;
var name = nameEl.GetString();
if (string.IsNullOrEmpty(name) || swaggerDoc.Tags.Any(t => t.Name == name)) continue;
var desc = tag.TryGetProperty("description", out var d) ? d.GetString() : null;
swaggerDoc.Tags.Add(new OpenApiTag { Name = name, Description = desc });
}
}
if (!root.TryGetProperty("paths", out var pathsEl)) return;
foreach (var pathProp in pathsEl.EnumerateObject())
{
var fullPath = pathProp.Name.StartsWith("/api/sl", StringComparison.Ordinal)
? pathProp.Name
: "/api/sl" + pathProp.Name;
var pathItem = new OpenApiPathItem();
foreach (var opProp in pathProp.Value.EnumerateObject())
{
if (!VerbMap.TryGetValue(opProp.Name, out var verb)) continue;
pathItem.Operations[verb] = ParseOperation(opProp.Value);
}
if (pathItem.Operations.Count > 0)
swaggerDoc.Paths[fullPath] = pathItem;
}
}
private static OpenApiOperation ParseOperation(JsonElement el)
{
var op = new OpenApiOperation
{
Summary = el.TryGetProperty("summary", out var s) ? s.GetString() : null,
Description = el.TryGetProperty("description", out var d) ? d.GetString() : null
};
if (el.TryGetProperty("tags", out var tags) && tags.ValueKind == JsonValueKind.Array)
{
foreach (var t in tags.EnumerateArray())
{
var name = t.GetString();
if (!string.IsNullOrEmpty(name))
op.Tags.Add(new OpenApiTag { Name = name });
}
}
if (el.TryGetProperty("parameters", out var parameters) && parameters.ValueKind == JsonValueKind.Array)
{
foreach (var p in parameters.EnumerateArray())
{
var param = new OpenApiParameter
{
Name = p.TryGetProperty("name", out var n) ? n.GetString() : null,
In = p.TryGetProperty("in", out var loc) ? ParameterLocationFrom(loc.GetString()) : null,
Required = p.TryGetProperty("required", out var req) && req.GetBoolean(),
Description = p.TryGetProperty("description", out var pd) ? pd.GetString() : null
};
if (p.TryGetProperty("schema", out var schema))
param.Schema = ParseSchema(schema);
op.Parameters.Add(param);
}
}
if (el.TryGetProperty("requestBody", out var body))
op.RequestBody = ParseRequestBody(body);
if (el.TryGetProperty("responses", out var responses))
{
foreach (var resp in responses.EnumerateObject())
{
op.Responses[resp.Name] = new OpenApiResponse
{
Description = resp.Value.TryGetProperty("description", out var rd)
? rd.GetString() ?? ""
: ""
};
}
}
return op;
}
private static OpenApiRequestBody? ParseRequestBody(JsonElement el)
{
if (!el.TryGetProperty("content", out var content)) return null;
var body = new OpenApiRequestBody();
foreach (var ct in content.EnumerateObject())
{
var media = new OpenApiMediaType();
if (ct.Value.TryGetProperty("schema", out var schema))
media.Schema = ParseSchema(schema);
body.Content[ct.Name] = media;
}
return body.Content.Count > 0 ? body : null;
}
private static OpenApiSchema ParseSchema(JsonElement el)
{
var schema = new OpenApiSchema();
if (el.TryGetProperty("type", out var t)) schema.Type = t.GetString();
if (el.TryGetProperty("description", out var d)) schema.Description = d.GetString();
if (el.TryGetProperty("$ref", out var r))
{
var refId = r.GetString()?.TrimStart('#', '/');
if (!string.IsNullOrEmpty(refId))
schema.Reference = new OpenApiReference { Id = refId, Type = ReferenceType.Schema };
}
return schema;
}
private static ParameterLocation? ParameterLocationFrom(string? loc) => loc switch
{
"query" => ParameterLocation.Query,
"path" => ParameterLocation.Path,
"header" => ParameterLocation.Header,
"cookie" => ParameterLocation.Cookie,
_ => null
};
private static string? TryLoadJson()
{
foreach (var candidate in ResolveCandidatePaths())
{
try
{
if (File.Exists(candidate))
return File.ReadAllText(candidate);
}
catch { /* next */ }
}
return null;
}
private static IEnumerable<string> ResolveCandidatePaths()
{
var roots = new[] { AppContext.BaseDirectory, Directory.GetCurrentDirectory() }
.Distinct(StringComparer.OrdinalIgnoreCase);
foreach (var root in roots)
{
yield return Path.GetFullPath(Path.Combine(root, "OpenApi", "simplelite-projection.json"));
yield return Path.GetFullPath(Path.Combine(root, "..", "..", "..", "Simple", "SimpleLite", "Docs", "openapi", "simplelite-projection.json"));
yield return Path.GetFullPath(Path.Combine(root, "..", "..", "Simple", "SimpleLite", "Docs", "openapi", "simplelite-projection.json"));
}
}
}