新版本 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 ...
随机推荐
- java并发编程--Synchronized的理解
synchronized实现锁的基础:Java中每一个对象都可以作为锁,具体表现为3种形式. (1)普通同步方法,锁是当前实例对象 (2)静态同步方法,锁是当前类的Class对象 (3)同步方法块,锁 ...
- 阿里内部推出Spring响应式微服务Boot2Cloud文档
今天要给大家推荐的是Spring响应式微服务SpringBoot2+Spring5+SpringCloud实战的内容,将从目录.主要内容和面向的读者三部分给大家介绍,希望大家能够喜欢!!!(本文整理自 ...
- 遍历 Dictionary,你会几种方式?
一:背景 1. 讲故事 昨天在 StackOverflow 上看到一个很有趣的问题,说: 你会几种遍历字典的方式,然后跟帖就是各种奇葩的回答,挺有意思,马上就要国庆了,娱乐娱乐吧,说说这种挺无聊的问题 ...
- [vue-webpack-template] webpack配置全局less引入
1. 项目模板webpack vue init webpack <项目名> 2. 安装依赖 除了less所需的less less-loader两个包以外,还需要安装style-resour ...
- Nginx+Gunicorn+Supervisor部署Flask应用
Flask 内置了简单的 Web 环境,让我们在开发的时候只需要专注于应用实现,而真正要在生产环境运行时这个简单的 Web 环境就不够用了,还需要一系列操作才能让 Web 应用高效的运行起来.现在记录 ...
- Python练习题 040:Project Euler 012:有超过500个因子的三角形数
本题来自 Project Euler 第12题:https://projecteuler.net/problem=12 # Project Euler: Problem 12: Highly divi ...
- cdev_alloc与cdev_init区别
struct cdev *cdev_alloc(void) { struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL); if (p) { ...
- Python-joypy和 R-ggridges 峰峦图制作
本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 以下文章来源于DataCharm,作者 宁海涛 转载地址 https://www ...
- C语言中i++和++i的区别
这一篇更详细: 转载:https://blog.csdn.net/Bug_fuck/article/details/85229229 C语言中++i和i++是有区别的!快速理解的话就是用一句话概括:1 ...
- npm包管理器报错-npm ERR! Response timeout while trying to fetch https://registry.npmjs.org/@XXX(over 30000ms)
由于这两天买的新电脑在短期内频频蓝屏.卡机,不得不把自己其他的本本拿出来换上,但是程序员换电脑是真的痛苦,其他不说就说一个配环境 真的折腾哈 我是一名前端菜鸟,现在自己的本本上使用的是npm包管理工具 ...