1. 程式人生 > >基於.NetCore3.1搭建專案系列 —— 使用Swagger匯出文件 (番外篇)

基於.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.原始碼