1. 程式人生 > >NET 5.0 Swagger API 自動生成MarkDown文件

NET 5.0 Swagger API 自動生成MarkDown文件

[TOC] > 基於 Swashbuckle.AspNetCore ,根據SwaggerGenerators生成的文件生成 MarkDown 文件。 > > 文件功能: > > - [x] JSON 資料格式展示 Request 、Response 資料結構(支援實體多級引用) > > - [x] 生成 Request Body 、Response Body 示例引數 > - [x] 支援特性過濾Controller、Action ## 1、SwaggerDoc引用 ### 主要介面 ```C# public interface ISwaggerDocGenerator { Task GetSwaggerDocStreamAsync(string name); string GetSwaggerDoc(string name); } ``` ### 介面實現 ```C# /// /// SwaggerDocGenerator ///
public class SwaggerDocGenerator : ISwaggerDocGenerator { private readonly SwaggerGenerator _generator; private IDictionary Schemas; const string contentType = "application/json"; /// /// SwaggerDocGenerator /// /// public SwaggerDocGenerator(SwaggerGenerator swagger) { _generator = swagger; } /// /// 生成MarkDown ///
/// public string GetSwaggerDoc(string name) { if (string.IsNullOrEmpty(name)) throw new Exception("name is null !"); var document = _generator.GetSwagger(name); if (document == null) throw new Exception("swagger is null !"); Schemas = document.Components.Schemas; var markDown = new StringBuilder(); markDown.AppendLine(document?.Info?.Title.H1());//文件標題 markDown.AppendLine(document?.Info?.Description.Ref1());//文件描述 foreach (var path in document.Paths) { var openApiPath = path.Value; var (flag, method, operation) = GetApiOperation(openApiPath); if (flag == false) continue; var row = new StringBuilder(); var url = path.Key; var title = operation.Summary ?? url; var httpMethod = method; var query = GetParameters(operation.Parameters); var (requestExapmle, requestSchema) = GetRequestBody(operation.RequestBody); var (responseExapmle, responseSchema) = GetResponses(operation.Responses); row.AppendLine(title.H2());//介面名稱 row.AppendLine("基本資訊".H3().NewLine());//基本資訊 row.AppendLine($"{"介面地址:".B()}{url}".Li().NewLine()); row.AppendLine($"{"請求方式:".B()}{httpMethod}".Li().NewLine()); if (httpMethod == "Post" || httpMethod == "Put") { row.AppendLine($"{"請求型別:".B()}{contentType}".Li().NewLine()); } if (string.IsNullOrWhiteSpace(query) == false)//Query { row.AppendLine("Query".H3()); row.AppendLine(query); } if (string.IsNullOrWhiteSpace(requestSchema) == false)//RequestSchema { row.AppendLine("RequestSchema".H3()); row.AppendLine(requestSchema.Code()); } if (string.IsNullOrWhiteSpace(requestExapmle) == false)//RequestBody { row.AppendLine("RequestBody".H3()); row.AppendLine(requestExapmle.Code()); } if (string.IsNullOrWhiteSpace(responseSchema) == false)//ResponseSchema { row.AppendLine("ResponseSchema".H3()); row.AppendLine(responseSchema.Code()); } if (string.IsNullOrWhiteSpace(responseExapmle) == false)//ResponseBody { row.AppendLine("ResponseBody".H3()); row.AppendLine(responseExapmle.Code()); } if (string.IsNullOrWhiteSpace(row.ToString()) == false) markDown.AppendLine(row.ToString().Br()); } return markDown.ToString(); } private (bool isSuccesss, string method, OpenApiOperation openApiOperation) GetApiOperation(OpenApiPathItem openApiPathItem) { var operations = openApiPathItem.Operations; OpenApiOperation operation; OperationType? operationType = null; if (operations.ContainsKey(OperationType.Get)) operationType = OperationType.Get; else if (operations.ContainsKey(OperationType.Post)) operationType = OperationType.Post; else if (operations.ContainsKey(OperationType.Put)) operationType = OperationType.Put; else if (operations.ContainsKey(OperationType.Patch)) operationType = OperationType.Patch; else if (operations.ContainsKey(OperationType.Delete)) operationType = OperationType.Delete; var flag = operations.TryGetValue(operationType.Value, out operation); return (flag, operationType.Value.ToString(), operation); } private string GetParameters(IList apiParameters) { string str = null; var isFirst = true; foreach (var parameter in apiParameters) { var queryTitle = "|引數名稱|引數型別|描述|".NewLine(); queryTitle += "|:----:|:----:|:----:|".NewLine(); var queryStr = $"|{parameter.Name}|{parameter.Schema.Type}|{parameter.Description}|".NewLine(); str += isFirst ? $"{queryTitle}{queryStr}" : queryStr; isFirst = false; } return str; } private (string exampleJson, string schemaJson) GetRequestBody(OpenApiRequestBody body) { string exampleJson = null, schemaJson = null; if (body != null && body.Content.ContainsKey(contentType)) { var schema = body.Content[contentType].Schema; exampleJson += GetExapmple(schema).ToJson(); schemaJson += GetModelInfo(schema, (id) => GetModelTProc(id)).ToJson(); } return (exampleJson, schemaJson); } private (string exampleJson, string schemaJson) GetResponses(OpenApiResponses body) { string exampleJson = null, schemaJson = null; if (body != null && body["200"].Content.ContainsKey(contentType)) { var schema = body["200"].Content[contentType].Schema; exampleJson += GetExapmple(schema).ToJson(); schemaJson += GetModelInfo(schema, (id) => GetModelTProc(id, false)).ToJson(); } return (exampleJson, schemaJson); } private object GetExapmple(OpenApiSchema apiSchema) { object exapmle = null; if (apiSchema.Type == null && apiSchema.Reference != null)//object { var key = apiSchema?.Reference?.Id; exapmle = GetModelExample(key); } else if (apiSchema.Type == "array" && apiSchema.Items != null)//array { var key = apiSchema?.Items?.Reference?.Id; if (key != null) exapmle = new[] { GetModelExample(key) }; else if (key == null && apiSchema.Items.Type != null) exapmle = new[] { GetDefaultValue(apiSchema.Items.Type) }; } else { exapmle = GetDefaultValue(apiSchema.Type); } return exapmle; } private object GetModelExample(string key) { if (key != null && Schemas.ContainsKey(key)) { var schema = Schemas.FirstOrDefault(x => x.Key == key).Value; var exapmle = new ModelExample(); if (schema.Properties.Any()) { foreach (var item in schema.Properties) { if (item.Value.Reference != null && Schemas.FirstOrDefault(x => x.Key == item.Value.Reference.Id).Value.Enum.Count == 0) { var objKey = item.Value.Reference.Id; exapmle.Add(item.Key, GetModelExample(objKey)); } else if (item.Value.Items != null) { var arrayKey = item.Value.Items.Reference.Id; exapmle.Add(item.Key, new[] { GetModelExample(arrayKey) }); } else { if (item.Value.Reference != null && Schemas.FirstOrDefault(x => x.Key == item.Value.Reference.Id).Value.Enum.Count != 0) exapmle.Add(item.Key, 0); else exapmle.Add(item.Key, GetDefaultValue(item.Value.Format ?? item.Value.Type)); } } } return exapmle; } return null; } private object GetModelInfo(OpenApiSchema apiSchema, Func func) { object info = null; var key = ""; if (apiSchema.Type == null && apiSchema.Reference != null)//object key = apiSchema?.Reference?.Id; else if (apiSchema.Type == "array" && apiSchema.Items != null)//array key = apiSchema?.Items?.Reference?.Id ?? apiSchema.Items.Type; else if (apiSchema.Type != null) key = apiSchema.Type; if (key != null) info = func(key); return info; } private object GetModelTProc(string key, bool isShowRequired = true) { if (key != null) { if (Schemas.ContainsKey(key)) { var schema = Schemas.FirstOrDefault(x => x.Key == key).Value; var info = new Dictionary(); if (schema.Properties.Any()) { foreach (var item in schema.Properties) { object obj = item.Value.Format ?? item.Value.Type ?? "object"; if (item.Value.Reference != null && Schemas.FirstOrDefault(x => x.Key == item.Value.Reference.Id).Value.Enum.Count == 0) { var objKey = item.Value.Reference.Id; obj = GetModelTProc(objKey, isShowRequired); } else if (item.Value.Items != null) { var arrayKey = item.Value.Items.Reference.Id; obj = GetModelTProc(arrayKey, isShowRequired); } else { if (item.Value.Reference != null && Schemas.FirstOrDefault(x => x.Key == item.Value.Reference.Id).Value.Enum.Count != 0) obj = item.Value.Reference.Id; } if (isShowRequired) { var requestModelInfo = new RequestModelInfo { 引數型別 = obj, 描述 = item.Value.Description, 是否必傳 = schema.Required.Any(x => x == item.Key) }; info.Add(item.Key, requestModelInfo); } else { var responseModelInfo = new ResponseModelInfo { 引數型別 = obj, 描述 = item.Value.Description }; info.Add(item.Key, responseModelInfo); } } } return info; } else { return key; } } return null; } private Assembly GetAssembly() { return Assembly.GetExecutingAssembly(); } private bool Valid(string name) { var types = GetAssembly().GetTypes().Where(x => x.Name.EndsWith("Controller") && x.IsDefined(typeof(T))).Select(x => x.Name).ToArray(); return types.Any(x => x.ToLower().Contains(name.ToUpper())); } private object GetDefaultValue(string type) { var number = new string[] { "byte", "decimal", "double", "enum", "float", "int32", "int64", "sbyte", "short", "uint", "ulong", "ushort" }; if (number.Any(x => type == x)) return 0; if (type == "string") return "string"; if (type == "bool" || type == "boolean") return false; if (type == "date-time") return DateTime.Now; return null; } public async Task GetSwaggerDocStreamAsync(string name) { using var stream = new MemoryStream(); using var sw = new StreamWriter(stream); var content = GetSwaggerDoc(name); await sw.WriteLineAsync(content); return stream; } } ``` ## 2、Startup配置 ### 註冊SwaggerDoc服務 ```C# services.AddSwaggerDoc();//(用於MarkDown生成) ``` ### 註冊Swagger服務 ```C# services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "Swagger API 示例文件", Version = "v1",Description="API文件全部由程式碼自動生成" }); c.IncludeXmlComments("Samples.xml"); }); ``` ### 引用Swagger中介軟體 ```C# app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Samples v1")); ``` ## 3、生成MarkDown ```C# /// /// SwaggerController ///
[Route("api/[controller]/[action]")] [ApiController] public class SwaggerController : ControllerBase { /// /// API文件匯出 /// [HttpGet] public async Task SwaggerDoc([FromServices] ISwaggerDocGenerator swaggerDocGenerator, [FromServices] IWebHostEnvironment environment) { var stream = await swaggerDocGenerator.GetSwaggerDocStreamAsync("v1"); var mime = "application/octet-stream"; var name = "SwaggerDoc.md"; return File(stream.ToArray(), mime,name); } } ``` ## 4、生成示例 ![SwaggerDoc.png](https://github.com/lwc1st/SwaggerDoc/blob/master/Doc/SwaggerDoc.png?raw=true) ## 5、MarkDown轉PDF 我是用的是 [typora](https://www.typora.io/) 編輯器,下載 [pandoc](https://github.com/jgm/pandoc/releases) 外掛可以實現Marddown格式轉換為PDF功能(免費) 如果需要樣式調整,可以去https://theme.typora.io/ 選選 ![ToPDF.png](https://github.com/lwc1st/SwaggerDoc/blob/master/Doc/ToPDF.png?raw=true) ## 完整專案示例 地址(可以直接執行): https://github.com/lwc1st/Sw