• 基于.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='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 + @"FilesTempFiles";
                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.源码下载

  • 相关阅读:
    Node.js Express 框架
    Node.js RESTful API
    vim中自动格式化代码
    如何去掉linux配置文件的注释行和空行
    ImmutableJS
    JavaScript的相关知识
    React容器组件和展示组件
    node.js
    svg学习
    Redux 基础概念
  • 原文地址:https://www.cnblogs.com/i3yuan/p/12633768.html
Copyright © 2020-2023  润新知