基于.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, , bytes.Length);
// 设置当前流的位置为流的开始
stream.Seek(, 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.源码下载
基于.NetCore3.1搭建项目系列 —— 使用Swagger导出文档 (番外篇)的更多相关文章
- 基于.NetCore3.1搭建项目系列 —— 使用Swagger导出文档 (补充篇)
前言 在上一篇导出文档番外篇中,我们已经熟悉了怎样根据json数据导出word的文档,生成接口文档,而在这一篇,将对上一篇进行完善补充,增加多种导出方式,实现更加完善的导出功能. 回顾 1. 获取Sw ...
- 基于.NetCore3.1搭建项目系列 —— 使用Swagger做Api文档 (上篇)
前言 为什么在开发中,接口文档越来越成为前后端开发人员沟通的枢纽呢? 随着业务的发张,项目越来越多,而对于支撑整个项目架构体系而言,我们对系统业务的水平拆分,垂直分层,让业务系统更加清晰,从而产生一系 ...
- 基于.NetCore3.1搭建项目系列 —— 使用Swagger做Api文档 (下篇)
前言 回顾上一篇文章<使用Swagger做Api文档 >,文中介绍了在.net core 3.1中,利用Swagger轻量级框架,如何引入程序包,配置服务,注册中间件,一步一步的实现,最终 ...
- JavaWeb项目中集成Swagger API文档
1.增加依赖 <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-sw ...
- ABP框架搭建项目系列教程基础版完结篇
返回总目录<一步一步使用ABP框架搭建正式项目系列教程> 经过前面十二篇的基础教程,现在终于该做个总结了. 回顾 第一篇,我们建议新手朋友们先通过ABP官网的启动模板生成解决方案,因为这样 ...
- 分享一个集成在项目中的REST APIs文档框架swagger
1 为什么是使用swagger? 1-1 当后台开发人员开发好接口,是不是还要重新书写一份接口文档提给前端人员,当然对于程序员最不喜欢的就是书写文档(当然文档是必须的,有利于项目的维护) 1-2 当后 ...
- 使用Swagger 搭建高可读性ASP.Net WebApi文档
一.前言 在最近一个商城项目中,使用WebApi搭建API项目.但开发过程中,前后端工程师对于沟通接口的使用,是非常耗时的.之前也有用过Swagger构建WebApi文档,但是API文档的可读性并不高 ...
- springboot+swagger接口文档企业实践(上)
目录 1.引言 2.swagger简介 2.1 swagger 介绍 2.2 springfox.swagger与springboot 3. 使用springboot+swagger构建接口文档 3. ...
- Spring系列(零) Spring Framework 文档中文翻译
Spring 框架文档(核心篇1和2) Version 5.1.3.RELEASE 最新的, 更新的笔记, 支持的版本和其他主题,独立的发布版本等, 是在Github Wiki 项目维护的. 总览 历 ...
随机推荐
- leetcode 249 250 set和map的简单用法
leetcode249,利用了STL中的set class Solution { public: vector<int> intersection(vector<int>&am ...
- 必备技能四、ajax及token
转https://segmentfault.com/a/1190000008470355?utm_source=tuicool&utm_medium=referral 转 https://ww ...
- Nginx之负载均衡配置(一)
前文我们聊了下nginx作为反向代理服务器,代理后端动态应用服务器的配置,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/12430543.html:今天我们来聊 ...
- vue用template还是JSX?
各自特点 template 模板语法(HTML的扩展) 数据绑定使用Mustache语法(双大括号) <span>{{title}}<span> JSX JavaScript的 ...
- Vue2.0 【第二季】第2节 Vue.extend构造器的延伸
目录 Vue2.0 [第二季]第2节 Vue.extend构造器的延伸 一.什么是Vue.extend 二.自定义无参数标签 三.挂载到普通标签上 Vue2.0 [第二季]第2节 Vue.extend ...
- Docker 技术系列之安装多版本Mysql5.6和Mysql5.7
大家好,后面的就不是关于MAC专有的内容,基本是跟Java环境,基础技术方面有关.所以这个教程对于在linux系统还是macOS都是通用的,不用担心. 上一篇,我们安装好对应的Docker之后,感受到 ...
- Python 3.9 新特性:任意表达式可作为装饰器!
一个月前(2月20日),一则新的 PEP 没有受到任何阻碍就被官方采纳了,这么快的速度,似乎并不多见. 然而,更为高效率的是,仅在半个月内,它的实现就被合入了代码仓.也就是说,我们最快有望在 3 天后 ...
- java-3个例子(新手)
//创建的一个包名. package ri0318; //创建的一个类. public class Li3 { //公共静态的主方法. public static void main(String[] ...
- F版本SpringCloud 3—大白话Eureka服务注册与发现
引用:服务注册与发现,就像是租房子一样 前言 今天洛阳下雨了,唉,没有想到有裹上了羽绒服,不穿冷穿了热的尴尬温度.上学工作这么多年都在外面,家里竟然没有一件春天的外套. 日常闲聊之后,开始今天的芝士环 ...
- python之路---装饰器函数
阅读目录 楔子 装饰器的形成过程 开放封闭原则 谈装饰器主要功能和装饰器固定结构 带参数的装饰器 多个装饰器装饰一个函数 返回顶部 楔子 作为一个会写函数的python开发,我们从今天开始要去公司上班 ...