基於.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 + @"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.源碼下載