前言
本来在Swagger的基础上,前后端开发人员在开发生产期间,可以借此进行更加便捷的沟通交流。可是总有些时候,遇到一些难缠的,又不讲道理,偏偏觉得将Swagger文档地址丢给客户会不够正式!死活要一份word文档。
可是这个时候,如果接口数量上百个,甚至更多,一个一个手动输入word,那将是一笔耗时的工作。但却有什么办法可以解决呢?
对了,利用Swagge生成的Json文件转换为word文档不就可以了吗?
思路
1. 获取Swagger接口文档的Json文件
2. 解析Json文件数据填充到Html的表格中
3.根据生成的html转work文档
开始
一、根据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[] 转成 Streampublic static Stream BytesToStream(byte[] bytes)
{
Stream stream = new MemoryStream(bytes);
return stream;
}
}
四、最终效果
将html转换为word后,我们就可以看到带有 .doc 的效果了!差不多是如下效果
总结
1. 到这基本就结束了,通过简易的几个接口的方式,展示了如何通过将Swagger接口文档生成word文档。可以根据自己的html模板生成各式的word样式文档说明。
2.写这篇番外主要是因为之前介绍了关于如何使用Swagger生成在线文档,但实际工作中,可能也会遇到这种要各种正式word文档的客户,所以在此分享一些想法和思路,同时希望大家不吝指教。