SwaggerUI看烦了,IGeekFan.AspNetCore.Knife4jUI 帮你换个新皮肤
背景
好像是上周四,看到微信群有人说java有轮子swagger-bootstrap-ui,而c#,就是找不到。
于是我一看,就说大话:“这个只是一套UI,他这个有开源地址么”
被@at说:你试试...
当天晚上就把swagger-ui, Knife4j,Swashbuckle.AspNetCore项目的源码都下载下来研究下,看看能不能集成到AspNETCore下,这样我们就能给Swagger UI换套新皮肤。
knife4j
knife4j 是swagger-bootstrap-ui库的升级版,作者已全面升级,全部以knife4j命名。
Gitee上也有2.8K
- 效果图
IGeekFan.AspNetCore.Knife4jUI
他是swagger ui 库:knife4j UI 的.NET Core封装,支持 .NET Core3.0+或.NET Standard2.0。
概念对应关系如下
功能 | c# | java |
---|---|---|
实现swagger规范 | Swashbuckle.AspNetCore | spring-fox |
封装成nuget包/maven包的UI库 | Swashbuckle.AspNetCore.SwaggerUI | knife4j-v3-spring-ui |
UI库 | swagger-ui-dist | knife4j-vue-v3(swagger v3版本) |
注意
swagger v2和v3版本不一样,我只实现了swagger v3版本的封装。
源码下载
Swashbuckle.AspNetCore.SwaggerUI。
通过中间件SwaggerUIM中间件iddleware,Invoke方法中,替换了Index.html中的%(DocumentTitle) %(HeadContent),%(ConfigObject)等等 。
private readonly SwaggerUIOptions _options;
//xxx
public async Task Invoke(HttpContext httpContext)
{
//xxx
if (httpMethod == "GET" && Regex.IsMatch(path, $"^/{Regex.Escape(_options.RoutePrefix)}/?index.html$"))
{
await RespondWithIndexHtml(httpContext.Response);
return;
}
//xxx
}
private async Task RespondWithIndexHtml(HttpResponse response)
{
response.StatusCode = 200;
response.ContentType = "text/html;charset=utf-8";
using (var stream = _options.IndexStream())
{
// Inject arguments before writing to response
var htmlBuilder = new StringBuilder(new StreamReader(stream).ReadToEnd());
foreach (var entry in GetIndexArguments())
{
htmlBuilder.Replace(entry.Key, entry.Value);
}
await response.WriteAsync(htmlBuilder.ToString(), Encoding.UTF8);
}
}
private IDictionary<string, string> GetIndexArguments()
{
return new Dictionary<string, string>()
{
{ "%(DocumentTitle)", _options.DocumentTitle },
{ "%(HeadContent)", _options.HeadContent },
{ "%(ConfigObject)", JsonSerializer.Serialize(_options.ConfigObject, _jsonSerializerOptions) },
{ "%(OAuthConfigObject)", JsonSerializer.Serialize(_options.OAuthConfigObject, _jsonSerializerOptions) }
};
}
在index.html中。
<title>%(DocumentTitle)</title>
var configObject = JSON.parse('%(ConfigObject)');
var oauthConfigObject = JSON.parse('%(OAuthConfigObject)');
当我们写的aspnetcore项目集成swagger组件后,只会有一个ajax的异步请求
knife4j-v3-spring-ui
效果(2.X版):http://knife4j.xiaominfo.com/doc.html
由于官方也没有v3的demo,我们可以暂时通过v2分析,发现他有3个异步请求,有一个请求返回相似的。另一个则是swagger的配置项,可以发现,返回值与SwaggerUIOptions一致。
功能 | c# (swagger v3) | java(swagger v2) |
---|---|---|
获取分组配置 | 无 | /swagger-resources |
swagger配置项 | 无 | /swagger-resources/configuration/ui |
api文档 | https://api.igeekfan.cn/swagger/v1/swagger.json | /v2/api-docs?group=2.X版本 |
结构如下。
[
{
"name":"2.X版本",
"url":"/v2/api-docs?group=2.X版本",
"swaggerVersion":"2.0",
"location":"/v2/api-docs?group=2.X版本"
},
{
"name":"分组接口",
"url":"/v2/api-docs?group=分组接口",
"swaggerVersion":"2.0",
"location":"/v2/api-docs?group=分组接口"
},
{
"name":"默认接口",
"url":"/v2/api-docs?group=默认接口",
"swaggerVersion":"2.0",
"location":"/v2/api-docs?group=默认接口"
}
]
- swagger 配置项
- http://knife4j.xiaominfo.com/swagger-resources/configuration/ui
请求方法: GET
{
"deepLinking":true,
"displayOperationId":false,
"defaultModelsExpandDepth":1,
"defaultModelExpandDepth":1,
"defaultModelRendering":"example",
"displayRequestDuration":false,
"docExpansion":"none",
"filter":false,
"operationsSorter":"alpha",
"showExtensions":false,
"tagsSorter":"alpha",
"validatorUrl":"",
"apisSorter":"alpha",
"jsonEditor":false,
"showRequestHeaders":false,
"supportedSubmitMethods":[
"get",
"put",
"post",
"delete",
"options",
"head",
"patch",
"trace"
]
}
接下来我们看下knife4j,可以看到,他有knife4j-vue-v3项目,这个是swagger v3版本的vue实现。
我们打开knife4j-vue-v3项目,修改配置项vue.config.js,devServer 反向代理的地址(后台地址)
proxy: {
"/": {
target: 'http://localhost:5000/',
ws: true,
changeOrigin: true
}
}
安装依赖,并运行他
yarn install
yarn serve
我们会看到一个请求错误。Knife4j文档请求异常,因为后台并没有:'/v3/api-docs/swagger-config'。
也就是上文中的/swagger-resources/configuration/ui,我们可以在SwaggerUIMiddleware中间件获取这些参数,原本是通过替换字符串,现在,我们可以写一个api。怎么写呢。
下载Swashbuckle.AspNetCore的源码,打开Swashbuckle.AspNetCore.sln。
我们尝试修改Swashbuckle.AspNetCore.SwaggerUI项目中,SwaggerUIMiddleware中的源码。
Invoke方法增加如下处理,将配置项直接返回json串。
if (httpMethod == "GET" && Regex.IsMatch(path, $"^/v3/api-docs/swagger-config$"))
{
await httpContext.Response.WriteAsync(JsonSerializer.Serialize(_options.ConfigObject, _jsonSerializerOptions));
return;
}
在swagger v3 版本中,/v3/api-docs/swagger-config,返回了分组信息,urls字段。
效果如下
设置test/WebSites/Basic项目为启动项目,运行后,打开了http://localhost:5000/index.html,这个还是原本的swagger ui,我们打开http://localhost:8080/#/home,前台依旧提示有问题。
AddSwaggerGen 需要增加Server,前台判断有BUG,非空。
servers.length得到的是0,问号表达式就会执行后面的servers[0].url,
临时方案
services.AddSwaggerGen(c =>
{
c.AddServer(new OpenApiServer()
{
Url = "",
Description = "v1"
});
});
但还有一个问题,前台根据operationId生成的路由, [HttpPost(Name = "CreateProduct")]比如CreateProduct。有些没有设置 Name的,点击后就会出现空白界面。
增加CustomOperationIds的配置,通过反射获取方法名。
services.AddSwaggerGen(c =>
{
//xx
c.CustomOperationIds(apiDesc =>
{
return apiDesc.TryGetMethodInfo(out MethodInfo methodInfo) ? methodInfo.Name : null;
});
});
解决了这些问题。
我们创建一个新类库,起名IGeekFan.AspNetCore.Knife4jUI
将前端打包。修改打包文件配置,vue.config.js
assetsDir: "knife4j",
indexPath: "index.html"
打包
yarn run build
复制到根目录,设置为嵌入文件,删除不需要的images和txt文本。
<ItemGroup>
<EmbeddedResource Include="knife4j/**/*" />
<EmbeddedResource Include="favicon.ico" />
<EmbeddedResource Include="index.html" />
</ItemGroup>
将后台Swashbuckle.AspNetCore.SwaggerUI的代码复制过来,全部重命名。比如中间件名字为
SwaggerUIMiddleware -> Knife4jUIMiddleware。即SwaggerUI都改成Knife4jUI。
Knife4jUIMiddleware修改位置
private const string EmbeddedFileNamespace = "IGeekFan.AspNetCore.Knife4jUI";
删除无用的替换变量,增加
Knife4UIOptions 修改
public Func<Stream> IndexStream { get; set; } = () => typeof(Knife4UIOptions).GetTypeInfo().Assembly
.GetManifestResourceStream("IGeekFan.AspNetCore.Knife4jUI.index.html");
Startup 中的Configure中间件
将UseSwaggerUI()改成UseKnife4UI()
app.UseKnife4UI(c =>
{
c.RoutePrefix = ""; // serve the UI at root
c.SwaggerEndpoint("/v1/api-docs", "V1 Docs");
c.SwaggerEndpoint("/gp/api-docs", "登录模块");
});
不用IGeekFan.AspNetCore.Knife4jUI也能实现?
当然,可以。
我们也能通过其他方式,在SwaggerUI的基础上,替换比如替换Index.html页面,自己打包前端UI,复制到项目中等。
将knife4j-vue-v3项目打包,放到wwwwroot目录中。
需要配置静态文件。
app.UseStaticFiles();
app.UseSwaggerUI(c =>
{
c.RoutePrefix = ""; // serve the UI at root
c.SwaggerEndpoint("/v1/api-docs", "V1 Docs");//这个配置无效。
c.IndexStream = () => new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot")).GetFileInfo("index.html").CreateReadStream();
});
重写/v3/api-docs/swagger-config路由
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapSwagger("{documentName}/api-docs");
endpoints.MapGet("/v3/api-docs/swagger-config", async (httpContext) =>
{
JsonSerializerOptions _jsonSerializerOptions = new JsonSerializerOptions();
_jsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
_jsonSerializerOptions.IgnoreNullValues = true;
_jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, false));
SwaggerUIOptions _options = new SwaggerUIOptions()
{
ConfigObject = new ConfigObject()
{
Urls = new List<UrlDescriptor>
{
new UrlDescriptor()
{
Url="/v1/api-docs",
Name="V1 Docs"
}
}
}
};
await httpContext.Response.WriteAsync(JsonSerializer.Serialize(_options.ConfigObject, _jsonSerializerOptions));
});
});
IGeekFan.AspNetCore.Knife4jUI指南
相关依赖项
knife4j
- knife4j-vue-v3(不是vue3,而是swagger-ui-v3版本)
Swashbuckle.AspNetCore
- Swashbuckle.AspNetCore.Swagger
- Swashbuckle.AspNetCore.SwaggerGen
Demo
快速开始
安装包
1.Install the standard Nuget package into your ASP.NET Core application.
Package Manager : Install-Package IGeekFan.AspNetCore.Knife4jUI
CLI : dotnet add package IGeekFan.AspNetCore.Knife4jUI
2.In the ConfigureServices method of Startup.cs, register the Swagger generator, defining one or more Swagger documents.
using System.Reflection;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using IGeekFan.AspNetCore.Knife4jUI;
SwaggerUI看烦了,IGeekFan.AspNetCore.Knife4jUI 帮你换个新皮肤的更多相关文章
- 除了Swagger UI,你还能选择 IGeekFan.AspNetCore.RapiDoc
IGeekFan.AspNetCore.RapiDoc 看到博客园上的这个文章,说了下Knife4J,评论里有人推荐RapiDoc,放了几个图,看了下,还不错. 心里 便有个想法,借着上次研究 Kni ...
- openerp模块收藏 auto_setup 自动帮你完成建新库时必做几个操作(转载)
auto_setup 自动帮你完成建新库时必做几个操作 原文:http://shine-it.net/index.php/topic,6777.0.html 模块地址: https://github. ...
- 看烦了VS2012的黑白调调了吗?换
VS2012的默认深色主题的确让整个IDE看起来很有气场,而且深色的主题保护眼睛,还是蛮不错的.但是看久了也会烦啊.虽然说重要的不是IDE看起来怎么样,而是写出来的代码质量怎么样,但一个好的环境也是会 ...
- 3分钟看完Java 8——史上最强Java 8新特性总结之第二篇 Stream API
目录 · 概况 · 切片(Slicing) · 映射(Mapping) · 匹配(Matching) · 查找(Finding) · 归约(Reducing) · 排序(Sorting) · 数值流( ...
- 3分钟看完Java 8——史上最强Java 8新特性总结之第四篇 其他新特性
目录 · 默认方法和静态方法 · 初步理解 · 应用模式 · 优先级问题 · Optional · CompletableFuture · 基本用法 · CompletableFuture与Strea ...
- 3分钟看完Java 8——史上最强Java 8新特性总结之第三篇 函数式编程技巧
目录 · 改写设计模式 · 策略模式(Strategy Pattern) · 模板方法模式(Template Method Pattern) · 观察者模式(Observer Pattern) · 责 ...
- 3分钟看完Java 8——史上最强Java 8新特性总结之第一篇 函数式编程基础
目录 · 行为参数化 · Lambda表达式 · 概况 · 函数式接口 · 类型推断 · 使用外层变量 · 方法引用 · 复合Lambda表达式 行为参数化 1. 理解函数式编程要先理解行为参数化. ...
- .NET Core基础篇之:集成Swagger文档与自定义Swagger UI
Swagger大家都不陌生,Swagger (OpenAPI) 是一个与编程语言无关的接口规范,用于描述项目中的 REST API.它的出现主要是节约了开发人员编写接口文档的时间,可以根据项目中的注释 ...
- RAID 概述
原创地址:http://www.cnblogs.com/jfzhu/p/3999283.html 转载请注明出处 独立硬盘冗余阵列(RAID, Redundant Array of Indep ...
随机推荐
- 通过hmail搭建一个内网测试的邮件服务器
我们测试的软件基本上都是支持邮件功能,如果你的测试环境是在外网的话那还好说,可以直接使用QQ邮箱.163邮箱等.但是如果是测试环境在内网,无法直接访问到外网的时候,搭建一个邮件服务器就很有必 ...
- 理解js中的几种设计模式
目录 工厂模式 构造函数模式 原型模式 组合使用构造函数模式和原型模式 动态原型模式 其它模式 工厂模式 function createPerson(name, age){ var o = new O ...
- 浅谈工业4.0背景下的空中数据端口,无人机3D 可视化系统的应用
前言 近年来,无人机的发展越发迅速,既可民用于航拍,又可军用于侦察,涉及行业广泛,把无人机想象成一个“会飞的传感器”,无人机就成了工业4.0的一个空中数据端口,大至地球物理.气象.农业数据.小至个人位 ...
- Java数组倒置
Java数组之 -- 数组倒置 方法一 : package mytest; public class test2 { public static void main(String[] args ...
- 金三银四,资深HR给面试者的十大建议
一.提前复习好你的专业知识 专业知识是最为重要的一点,拥有了坚实的专业基础,你才能迈向成功的彼岸. 因此,面试之前,一定一定要复习好专业知识.对自己学过的知识,要做一个概括,放在脑海中.茶余饭后,复习 ...
- 集训作业 洛谷P1032 字串变换
集训的题目有点多,先写困难的绿题吧(简单的应该想想就会了) 嗯,这个题看起来像个搜索呢(就是个搜索) 我们仔细想想就知道这个题肯定不能用深搜,可以优化的地方太少了,TLE是必然的. 那我们该怎么办呢? ...
- JavaScript动画实例:沿五角星形线摆动的小圆
五角星形线的笛卡尔坐标方程式可设为: r=10+(3*sin(θ*2.5))^2 x=r*cos(θ) y=r*sin(θ) (0≤θ≤2π) 根据这个曲线方程,在[0,2 ...
- Lucas定理 & Catalan Number & 中国剩余定理(CRT)
又双叒叕来水数论了 今天来学习\(Lucas \:\ \& \:\ Catalan Number\) 两者有着密切的联系(当然还有CRT),所以放在一起学习一下 \(Lucas\) 定义\(\ ...
- 【Nginx】如何使用Nginx实现MySQL数据库的负载均衡?看完我懂了!!
写在前面 Nginx能够实现HTTP.HTTPS协议的负载均衡,也能够实现TCP协议的负载均衡.那么,问题来了,可不可以通过Nginx实现MySQL数据库的负载均衡呢?答案是:可以.接下来,就让我们一 ...
- 动态DP,ddp
动态DP?动态动态规划? 个人理解:动态DP,就是普通DP加修改操作,然后就变成了个毒瘤题. 直接就着例题写吧. 例题 P4719 [模板]"动态 DP"&动态树分治 求树 ...
IGeekFan.AspNetCore.RapiDoc 看到博客园上的这个文章,说了下Knife4J,评论里有人推荐RapiDoc,放了几个图,看了下,还不错. 心里 便有个想法,借着上次研究 Kni ...
auto_setup 自动帮你完成建新库时必做几个操作 原文:http://shine-it.net/index.php/topic,6777.0.html 模块地址: https://github. ...
VS2012的默认深色主题的确让整个IDE看起来很有气场,而且深色的主题保护眼睛,还是蛮不错的.但是看久了也会烦啊.虽然说重要的不是IDE看起来怎么样,而是写出来的代码质量怎么样,但一个好的环境也是会 ...
目录 · 概况 · 切片(Slicing) · 映射(Mapping) · 匹配(Matching) · 查找(Finding) · 归约(Reducing) · 排序(Sorting) · 数值流( ...
目录 · 默认方法和静态方法 · 初步理解 · 应用模式 · 优先级问题 · Optional · CompletableFuture · 基本用法 · CompletableFuture与Strea ...
目录 · 改写设计模式 · 策略模式(Strategy Pattern) · 模板方法模式(Template Method Pattern) · 观察者模式(Observer Pattern) · 责 ...
目录 · 行为参数化 · Lambda表达式 · 概况 · 函数式接口 · 类型推断 · 使用外层变量 · 方法引用 · 复合Lambda表达式 行为参数化 1. 理解函数式编程要先理解行为参数化. ...
Swagger大家都不陌生,Swagger (OpenAPI) 是一个与编程语言无关的接口规范,用于描述项目中的 REST API.它的出现主要是节约了开发人员编写接口文档的时间,可以根据项目中的注释 ...
原创地址:http://www.cnblogs.com/jfzhu/p/3999283.html 转载请注明出处 独立硬盘冗余阵列(RAID, Redundant Array of Indep ...
我们测试的软件基本上都是支持邮件功能,如果你的测试环境是在外网的话那还好说,可以直接使用QQ邮箱.163邮箱等.但是如果是测试环境在内网,无法直接访问到外网的时候,搭建一个邮件服务器就很有必 ...
目录 工厂模式 构造函数模式 原型模式 组合使用构造函数模式和原型模式 动态原型模式 其它模式 工厂模式 function createPerson(name, age){ var o = new O ...
前言 近年来,无人机的发展越发迅速,既可民用于航拍,又可军用于侦察,涉及行业广泛,把无人机想象成一个“会飞的传感器”,无人机就成了工业4.0的一个空中数据端口,大至地球物理.气象.农业数据.小至个人位 ...
Java数组之 -- 数组倒置 方法一 : package mytest; public class test2 { public static void main(String[] args ...
一.提前复习好你的专业知识 专业知识是最为重要的一点,拥有了坚实的专业基础,你才能迈向成功的彼岸. 因此,面试之前,一定一定要复习好专业知识.对自己学过的知识,要做一个概括,放在脑海中.茶余饭后,复习 ...
集训的题目有点多,先写困难的绿题吧(简单的应该想想就会了) 嗯,这个题看起来像个搜索呢(就是个搜索) 我们仔细想想就知道这个题肯定不能用深搜,可以优化的地方太少了,TLE是必然的. 那我们该怎么办呢? ...
五角星形线的笛卡尔坐标方程式可设为: r=10+(3*sin(θ*2.5))^2 x=r*cos(θ) y=r*sin(θ) (0≤θ≤2π) 根据这个曲线方程,在[0,2 ...
又双叒叕来水数论了 今天来学习\(Lucas \:\ \& \:\ Catalan Number\) 两者有着密切的联系(当然还有CRT),所以放在一起学习一下 \(Lucas\) 定义\(\ ...
写在前面 Nginx能够实现HTTP.HTTPS协议的负载均衡,也能够实现TCP协议的负载均衡.那么,问题来了,可不可以通过Nginx实现MySQL数据库的负载均衡呢?答案是:可以.接下来,就让我们一 ...
动态DP?动态动态规划? 个人理解:动态DP,就是普通DP加修改操作,然后就变成了个毒瘤题. 直接就着例题写吧. 例题 P4719 [模板]"动态 DP"&动态树分治 求树 ...