基于.NetCore3.1系列 —— 使用Swagger导出文档 (番外篇)
前言
回顾之前的两篇Swagger做Api接口文档,我们大体上学会了如何在net core3.1的项目基础上,搭建一套自动生产API接口说明文档的框架。
本来在Swagger的基础上,前后端开发人员在开发生产期间,可以借此进行更加便捷的沟通交流。可是总有些时候,遇到一些难缠的,又不讲道理,偏偏觉得将Swagger文档地址丢给客户会不够正式!死活要一份word文档。
可是这个时候,如果接口数量上百个,甚至更多,一个一个手动输入word,那将是一笔耗时的工作。但却有什么办法可以解决呢?
对了,利用Swagge生成的Json文件转换为word文档不就可以了吗?
思路
1. 获取Swagger接口文档的Json文件
2. 解析Json文件数据填充到Html的表格中
3.根据生成的html转work文档
模板
文档模板
|
|||||||||||
URL |
/api/Movie/AddMovie |
||||||||||
请求方式 |
Post |
||||||||||
参数名 |
参数类型 |
是否必填 |
说明 |
||||||||
id |
Query |
False |
影视ID |
||||||||
Name |
Query |
False |
电影名称 |
||||||||
Type |
Query |
False |
电影类型 |
||||||||
状态码 |
说明 |
||||||||||
200 |
Success |
||||||||||
示例 |
|||||||||||
请求参数 |
|
||||||||||
返回值 |
|
开始
一、根据Swagger版本获取Json数据
1.通过Swagger源码文件可以看到
可以拿到swagger生成的文档数据,所以我们可以新建一个控制器SwaggerController.cs,
private readonly SwaggerGenerator _swaggerGenerator; public SwaggerController(SwaggerGenerator swaggerGenerator) { _swaggerGenerator = swaggerGenerator; } /// <summary> /// 导出文件 /// </summary> /// <param name="type">文件类型</param> /// <param name="version">版本号V1</param> /// <returns></returns> [HttpGet] public FileResult ExportWord(string type,string version) { string contenttype = string.Empty; var model = _swaggerGenerator.GetSwagger(version); //1. 根据指定版本获取指定版本的json对象。 }
2. 在Startup.cs文件中,利用net core的ioc容器,注入SwaggerGenerator实例化,这样在后面的调用中可以直接使用这个方法
services.AddScoped<SwaggerGenerator>(); //注入SwaggerGenerator,后面可以直接使用这个方法
二、文件数据填充到Html的表格中
根据上面获取的model文件数据,这个时候,我们利用Razor文件,结合html的table模板,将数据遍历填充到页面中,生成完整的页面
Html模板
@using Swashbuckle.AspNetCore.Swagger; <!DOCTYPE html> <html> <head> <title>Swagger API文档代码文件</title> <style type='text/css'> table, table td, table th { border: 1px solid #000000; border-collapse: collapse; } table { table-layout: fixed; word-break: break-all; } tr { height: 20px; font-size: 12px; } </style> </head> <body> <div style='width:1000px; margin: 0 auto'> <span><i>Word接口文档</i></span> <h1 align="center">@Model.Info.Title</h1> <h1 align="center">接口文档 @Model.Info.Version</h1> <h4>联系方式</h4> <span>作者:@Model.Info.Contact.Name</span> <br> <a href="mailto:@Model.Info.Contact.Email" rel="noopener noreferrer" class="link">Send email to Xunit.Core</a> <br> <a href="@Model.Info.Contact.Url" target="_blank" rel="noopener noreferrer" class="link">@Model.Info.Contact.Name - Website</a> <br> <h3>接口描述</h3> <span>@Model.Info.Description</span> <br> <table border='1' cellspacing='0' cellpadding='0' style="table-layout: fixed; word-break: break-all;border: 1px solid #000000;border-collapse: collapse;" width='100%'> <tr style="border: 1px solid #000000;border-collapse: collapse;"> <td align="center" style="background-color: rgb(84, 127, 177);">说明</td> <td></td> </tr> <tr style="border: 1px solid #000000;border-collapse: collapse;"> <td align="center" style="background-color: rgb(84, 127, 177);">类型</td> <td></td> </tr> </table> @foreach (var item in Model.Paths) { if (item.Value.Operations != null) { foreach (var operation in item.Value.Operations) { <h3>@operation.Value.Summary</h3> <table border='1' cellspacing='0' cellpadding='0' width='100%' style="table-layout: fixed; word-break: break-all;border: 1px solid #000000;border-collapse: collapse;"> <tr style="background-color: rgb(84, 127, 177);" align="center"> <td colspan='5'></td> </tr> <tr style="border: 1px solid #000000;border-collapse: collapse;"> <td style="border: 1px solid #000000;border-collapse: collapse;">URL</td> <td colspan='4'>@item.Key</td> </tr> <tr style="border: 1px solid #000000;border-collapse: collapse;"> <td style="border: 1px solid #000000;border-collapse: collapse;">请求方式</td> <td colspan='4'> @operation.Key </td> </tr> @if (operation.Value.Parameters != null && operation.Value.Parameters.Count > 0) { <tr style="background-color: rgb(84, 127, 177);" align='center'> <td style="border: 1px solid #000000;border-collapse: collapse;">参数名</td> <td style="border: 1px solid #000000;border-collapse: collapse;">参数类型</td> <td style="border: 1px solid #000000;border-collapse: collapse;">是否必填</td> <td style="border: 1px solid #000000;border-collapse: collapse;" colspan='2'>说明</td> </tr> @foreach (var param in operation.Value.Parameters) { <tr align='center' style="border: 1px solid #000000;border-collapse: collapse;"> <td style="border: 1px solid #000000;border-collapse: collapse;">@param.Name</td> <td style="border: 1px solid #000000;border-collapse: collapse;">@param.In</td> <td style="border: 1px solid #000000;border-collapse: collapse;">@param.Required</td> <td style="border: 1px solid #000000;border-collapse: collapse;" colspan='2'>@param.Description</td> </tr> } } <tr style="background-color: rgb(84, 127, 177);" align='center'> <td style="border: 1px solid #000000;border-collapse: collapse;">状态码</td> <td style="border: 1px solid #000000;border-collapse: collapse;" colspan='4'>说明</td> </tr> @if (operation.Value.Responses != null && operation.Value.Responses.Count > 0) { foreach (var response in operation.Value.Responses) { <tr align='center' style="border: 1px solid #000000;border-collapse: collapse;"> <td style="border: 1px solid #000000;border-collapse: collapse;">@response.Key</td> <td style="border: 1px solid #000000;border-collapse: collapse;" colspan='4'>@response.Value.Description</td> </tr> } } <tr style="background-color: rgb(84, 127, 177);"> <td style="border: 1px solid #000000;border-collapse: collapse;" colspan='5'>示例</td> </tr> <tr style="height: 40px;" style="border: 1px solid #000000;border-collapse: collapse;"> <td style="background-color: rgb(84, 127, 177);">请求参数</td> <td style="border: 1px solid #000000;border-collapse: collapse;" colspan='4'></td> </tr> <tr style="height: 40px;" style="border: 1px solid #000000;border-collapse: collapse;"> <td style="background-color: rgb(84, 127, 177);">返回值</td> <td style="border: 1px solid #000000;border-collapse: collapse;" colspan='4'></td> </tr> </table> <br> } } } </div> </body> </html>
将数据遍历到静态页面中,
/// <summary> /// 将数据遍历静态页面中 /// </summary> /// <param name="templatePath">静态页面地址</param> /// <param name="model">获取到的文件数据</param> /// <returns></returns> public static string GeneritorSwaggerHtml(string templatePath, OpenApiDocument model) { var template = System.IO.File.ReadAllText(templatePath); var result = Engine.Razor.RunCompile(template, "i3yuan", typeof(OpenApiDocument), model); return result; }
三、根据生成的html转work文档
/// <summary> /// 静态页面转文件 /// </summary> /// <param name="html">静态页面html</param> /// <param name="type">文件类型</param> /// <param name="contenttype">上下文类型</param> /// <returns></returns> public Stream SwaggerConversHtml(string html, string type, out string contenttype) { string fileName = Guid.NewGuid().ToString() + type; //文件存放路径 string webRootPath = _hostingEnvironment.WebRootPath; string path = webRootPath + @"\Files\TempFiles\"; var addrUrl = path + $"{fileName}"; FileStream fileStream = null; var provider = new FileExtensionContentTypeProvider(); contenttype = provider.Mappings[type]; try { if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } var data = Encoding.Default.GetBytes(html); var stream = ByteHelper.BytesToStream(data); //创建Document实例 Document document = new Document(); //加载HTML文档 document.LoadFromStream(stream, FileFormat.Html, XHTMLValidationType.None); //保存为Word document.SaveToFile(addrUrl, FileFormat.Docx); document.Close(); fileStream = File.Open(addrUrl, FileMode.OpenOrCreate); var filedata = ByteHelper.StreamToBytes(fileStream); var outdata = ByteHelper.BytesToStream(filedata); return outdata; } catch (Exception) { throw; } finally { if (fileStream != null) fileStream.Close(); if (File.Exists(addrUrl)) File.Delete(addrUrl);//删掉文件 } }
public class ByteHelper { public static byte[] StreamToBytes(Stream stream) { byte[] bytes = new byte[stream.Length]; stream.Read(bytes, 0, bytes.Length); // 设置当前流的位置为流的开始 stream.Seek(0, SeekOrigin.Begin); return bytes; } /// 将 byte[] 转成 Stream public static Stream BytesToStream(byte[] bytes) { Stream stream = new MemoryStream(bytes); return stream; } }
四、最终效果
将html转换为word后,我们就可以看到带有 .doc 的效果了!差不多是如下效果
总结
1. 到这基本就结束了,通过简易的几个接口的方式,展示了如何通过将Swagger接口文档生成word文档。可以根据自己的html模板生成各式的word样式文档说明。
2.写这篇番外主要是因为之前介绍了关于如何使用Swagger生成在线文档,但实际工作中,可能也会遇到这种要各种正式word文档的客户,所以在此分享一些想法和思路,同时希望大家不吝指教。
3.后续还会不断修改和完善,可以更多的生成不同的文件类型和按需生成不同版本的接口文档,持续更新。。。
4 .注:搜索关注公众号【DotNet技术谷】--回复【文档生成器】,可获取本篇Swagger转换work文件
5. 参考资料:Spire.Doc文件 、Swagger开源地址
6.源码下载