新版本 swagger 组件中 Servers 的 坑
新版本 Swashbuckle swagger 组件中 Servers 的 坑
Intro
上周做了公司的项目升级,从 2.2 更新到 3.1, swagger 直接更新到了最新,swagger 用的组件是 Swashbuckle.AspNetCore
,然后遇到一个 swagger 的问题,
在本地测试是没问题的,但是部署在测试环境之后就会有问题,主要是 swagger 界面会多一个 servers 的选项,可能会导致 swagger 不能正常使用,下面详细介绍一下
Swagger "bug" reproduce
大概的问题是这样的,在本地环境是好的,在测试环境部署是有问题,测试环境部署之后的 swagger 界面大致如下:
很明显这个 servers 是有问题的,我们实际访问的地址是 https://testserver/swagger
这样的地址,但是 swagger 内部拼出来的 server 地址和实际访问的地址是不符的,swagger 生成的 open api 文档里也会有一个 servers 的属性,示例如下:
这会导致我们使用 swagger 调试 API 的时候会走一个错误的 server 地址,实际请求的地址是 sever 地址加上 api path,可以看一个示例
Dig the Source
Swashbuckle.AspNetCore
是开源的,我们就是扒一扒它的实现源码吧,我们用的是 5.6.3 版本,直接看 5.6.3 tag 对应的代码,可以找到 swagger 的中间件
在这里我们可以看到,再返回给客户端之前 open api 文档响应之前我们是可以看到,是会经过 PreSerializeFilters
处理的,我们再详细看一下 swaggerProvider.GetSwagger
的实现
实现代码在这里(可以通过服务注册找到对应的实现,也可以直接找对应接口的实现)
二者结合来看,servers 会根据用户请求来获取一个 server 地址,而当有 X-Forwarded-Host
请求头的时候如果没有按照 swagger 指定的规则这样进行请求头的转发就会导致有问题,而我们的测试环境也正是因为如此,测试环境有一层 LB,经过 LB 转发了 X-Forwarded-Host
和 X-Forwarded-Proto
请求头,但是没有转发 X-Forwarded-Port
所以经过 swagger 的处理之后,就从 https://testserver
变成了 https://testserver:80
这样
private string GetHostOrNullFromRequest(HttpRequest request)
{
if (!request.Headers.TryGetValue("X-Forwarded-Host", out StringValues forwardedHost))
return null;
var hostBuilder = new UriBuilder($"http://{forwardedHost[0]}");
if (request.Headers.TryGetValue("X-Forwarded-Proto", out StringValues forwardedProto))
hostBuilder.Scheme = forwardedProto[0];
if (request.Headers.TryGetValue("X-Forwarded-Port", out StringValues forwardedPort))
hostBuilder.Port = int.Parse(forwardedPort[0]);
return hostBuilder.Uri.ToString().Trim('/');
}
private string GetBasePathOrNullFromRequest(HttpRequest request)
{
var pathBuilder = new StringBuilder();
if (request.Headers.TryGetValue("X-Forwarded-Prefix", out StringValues forwardedPrefix))
pathBuilder.Append(forwardedPrefix[0].TrimEnd('/'));
if (request.PathBase.HasValue)
pathBuilder.Append(request.PathBase.Value.TrimEnd('/'));
return (pathBuilder.Length > 0)
? pathBuilder.ToString()
: null;
}
解决方案
从上面的源码中基本就可以分析出问题的原因来,解决的办法我觉得有下面几种:
- LB 转发的时候带上
X-Forwarded-Port
请求头,转发原始请求的端口号(需要 LB 转发自己能够控制,我们如果要配置还需要让 DevOps 的童鞋帮忙弄,如果完全是自己控制的就比较方便【推荐】) - 在使用 Swagger 中间件之前把
X-Forwarded-Port
请求头设置为443
(不够灵活,如果访问 LB 是 http 或者有特别的端口号就会有问题) - 在使用 swagger 中间件之前把
X-Forwarded-Host
请求头移除掉,这样就不会有 servers 这个属性了(感觉不够优雅) - 注册一个
PreSerializeFilter
把 Servers 清空,实现代码如下(【推荐】,没有 servers 属性的时候完全按请求 swagger 的 baseUrl 来作为 api 的前缀,示例代码如下)
app.UseSwagger(c =>
{
c.PreSerializeFilters.Add((doc, _) =>
{
doc.Servers?.Clear();
});
});
更新之后就没有 servers 属性了,和之前的版本保持一致了
More
我们使用的是 5.6.3 版本,应该从 5.6.0 开始都有这个问题,如果遇到了这个问题不要慌哈,参考上面的解决方案即可
我觉得 swagger 这样的实现方式不太友好,更好的实现应该结合微软的 ForwardHeaders
中间件来实现,Swagger 组件作者表示已经有计划,打算在 6.0 的时候更新结合微软的中间件来实现,详细可以参考 Github 上的 Issue https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1814
Reference
- https://github.com/domaindrivendev/Swashbuckle.AspNetCore
- https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/v5.6.3/src/Swashbuckle.AspNetCore.Swagger/SwaggerMiddleware.cs
- https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/v5.6.3/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs#L31
- https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1814
新版本 swagger 组件中 Servers 的 坑的更多相关文章
- Asp.Net Core WebApi中接入Swagger组件(初级)
开发WebApi时通常需要为调用我们Api的客户端提供说明文档.Swagger便是为此而存在的,能够提供在线调用.调试的功能和API文档界面. 环境介绍:Asp.Net Core WebApi + S ...
- 在vue组件中style scoped中遇到的坑
在uve组件中我们我们经常需要给style添加scoped来使得当前样式只作用于当前组件的节点.添加scoped之后,实际上vue在背后做的工作是将当前组件的节点添加一个像data-v-1233这样唯 ...
- vue js 在组件中对数组使用splice() 遇到的坑。。。
遇到的问题: 用el-dialog写了个子组件 要实现在子组件中增删数据 点击确定后把值返回给父组件 父组件在每次点开子组件时都会把自己的值传进去. //父组件传值 this.$refs.transf ...
- 微信小程序中的自定义组件 以及 相关的坑
Step1 我们初始化一个小程序(本示例基础版本库为 1.7 ),删掉里面的示例代码,并新建一个 components 文件夹,用于存放我们以后开发中的所用组件,今天我们的目的是实现一个 首页 组件, ...
- vue 组件中数组的更新
今天写项目时遇到的问题,瞬间就卡在那了 来还原一下: parent.vue: <template> <div> <button @click="change&q ...
- angular2的ElementRef在组件中获取不到
angular2的ElementRef在组件中获取不到 angular2不推荐操作dom,但是实际应用中不可避免的需要使用到dom操作,怎么操作,官方文档提供了一系列api(ElementRef,Vi ...
- vue中遇到的坑!!!!!
一 .vue安装的坑 报错时的常见问题 1.cnpm install 模块名 –save-dev(关于环境的,表现为npm run dev 启动不了)cnpm install 模块名 –save(关于 ...
- 总结微信小程序开发中遇到的坑
总结微信小程序开发中遇到的坑,一些坑你得一个一个的跳啊,/(ㄒoㄒ)/~~ 1,页面跳转和参数传递实例 首先说一下我遇到的需求有一个我的消息页面,里面的数据都是后端返回的,返回的数据大致如下,有一个是 ...
- vue中的css作用域、vue中的scoped坑点
一.css作用域 之前一直很困扰css的作用域问题,即使是模块化编程下,在对应的模块的js中import css进来,这个css仍然是全局的.导致在css中需要加上对应模块的html的id/class ...
随机推荐
- 自定义springboot - starter 实现日志打印,并支持动态可插拔
1. starter 命名规则: springboot项目有很多专一功能的starter组件,命名都是spring-boot-starter-xx,如spring-boot-starter-loggi ...
- 2020 Java开发者数据分析:中国已成为 Java 第一大国
最近知名开发工具供应商Jetbrains在Java 25周年之际,对开发群体做了一次有意思的数据分析. 全文内容可见:https://blog.jetbrains.com/idea/2020/09/a ...
- Tomcat 8.5安装
安装 打开Tomcat官网:http://tomcat.apache.org/,下载tar.gz压缩文件,下载后文件名是apache-tomcat- 8.5.23.tar.gz.使用root用户安装, ...
- Python-求序列长度和序列长度协议-len() __len__
len() 求序列的长度 print(len("beimenchuixue")) print(len([1, 2, 3])) __len__ 对象中实现这个方法,则 len() 方 ...
- Python下的图像处理库,你选哪个?
奥里给~ 转载:https://blog.csdn.net/chen801090/article/details/105795068/ 在进行数字图像处理时,我们经常需要对图像进行读取.保存.缩放.裁 ...
- 二进制部署Redis-5.07
Redis简介 Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理. 它支持字符串.哈希表.列表.集合.有序集合,位图,hyperloglogs等数据类 ...
- shell-变量的数值运算与特殊应用expr
1. expr(evaluate expressions)命令的用法: expr命令一般用于整数值,当也可用于字符串,用来求表达式变量的值,同时expr也是一个手工命令行计算器. 语法:expr ex ...
- docker-compose应用
docker-compose应用 需求编写compose模板文件,实现同时启动tomcat.mysql.redis容器. 1.编写模板文件 #新建文件夹mkdir -p /tzh/compose_te ...
- 制作西北地区地图数据并maskout
1.从全国地图数据中选中西北5省:打开bou2_4p.shp文件添加相应的图层(中国各省的行政区域),选中工具栏中的"通过矩形选择要素"工具,用鼠标点击选择要输出的图元,按住ctr ...
- day12 Pyhton学习
一.昨日内容回顾 1.函数名 函数名是一个变量名 可以作为集合类的元素 可以作为参数进行传递 def func(fn): fn() 可以作为返回值返回 def outer(): def inner( ...