解决通过 Nginx 无法打开 Knife4j 页面问题
问题描述
我们有一个项目使用了 Knife4j,依赖的版本为
1 | <dependency> |
这个版本的 Knife4j 使用了 SpringDoc,其获取 Swagger 配置的地址为 /v3/api-docs/swagger-config
,获取文档的地址为 /v3/api-docs
,如果项目有多个分组,获取单个分组,比如分组“1-动物”,的文档的地址为 /v3/api-docs/1-动物
。
在项目部署时在项目的前面放置了一台 Nginx 反向代理服务器,配置了当访问路径为 /example
时会将请求转发到我们自己的项目
1 | location /example/ { |
当访问 http://localhost/example/doc.html
时,Knife4j 能正常的访问 Swagger 的配置,即 http://localhost/example/v3/api-docs/swagger-config
,但是无法访问 http://localhost/v3/api-docs/1-动物
,因此无法打开 Knife4j 文档页面。
分析问题
我们打开 Knife4j 的前端工程 knife4j-vue
,在 BasicLayout.vue
在挂载完成后会调用 created()
方法
1 | created() { |
因为我们使用的是 SpringDoc,它将调用 initSpringDocOpenApi()
方法
1 | initSpringDocOpenApi() { |
在这个方法中会调用 initSwagger()
方法,这个方法的参数重有一个比较重要的是 springdoc: true
1 | initSwagger(options) { |
在 initSwagger()
方法中会创建 SwaggerBootstrapUi
对象,然后调用 main()
方法。
SwaggerBootstrapUi
对象定义在 Knife4jAsync.js
文件中,它的定义如下
1 | function SwaggerBootstrapUi(options) { |
我们看到在 this.springdoc
的值为 true
时,会从浏览器地址 http://localhost/example/doc.html
中截取出 http://localhost/example/
部分作为 basePath
,然后拼接上 v3/api-docs/swagger-config
部分得到 this.url
的值,即 http://localhost/example/v3/api-docs/swagger-config
。这也就解释了为什么获取 Swagger 的配置没有问题。
从前面的分析我们知道在创建了 SwaggerBootstrapUi
对象后会调用它的 main()
方法,我们跟踪它的方法调用链直到 analysisGroup()
方法,在这个方法中调用前面组装的 this.url
,即 http://localhost/example/v3/api-docs/swagger-config
获取分组信息
1 | SwaggerBootstrapUi.prototype.analysisGroup = function () { |
使用 this.url
作为参数调用 ajax()
方法获取的数据样本如下
1 | { |
在 analysisSpringDocOpenApiGroupSuccess()
中解析分组信息
1 | SwaggerBootstrapUi.prototype.analysisSpringDocOpenApiGroupSuccess = function (data) { |
这个方法比较长,只保留了我们关心的内容,做了这么几件事情,从返回的信息中 urls
中解析出 url
,构建 SwaggerBootstrapUiInstance
对象信息。
接下来 analysisGroup()
方法会调用 createGroupElement()
方法
1 | SwaggerBootstrapUi.prototype.createGroupElement = function () { |
这个方法会使用 analysisGroup()
方法构建的第一个 SwaggerBootstrapUiInstance
对象,用它调用 analysisApi()
方法
1 | SwaggerBootstrapUi.prototype.analysisApi = function (instance) { |
这个方法使用 SwaggerBootstrapUiInstance
对象中的 url
,即 /v3/api-docs/1-动物
,自动拼接上域名/端口部分,即 http://loacalhost
,组成完整的地址 http://loacalhost/v3/api-docs/1-动物
获取文档信息。
到这里我们就可以看到从 analysisSpringDocOpenApiGroupSuccess
到 createGroupElement
到 analysisApi
没有一个地方像 SwaggerBootstrapUi()
方法中一样去获取或构建 basePath
部分,即 http://loacalhost/example/
,然后拼接 v3/api-docs/1-动物
。
解决问题
那么我们怎么解决这个问题呢?一种方法是修改 knife4j-vue
的源码然后重新打包。这里介绍另外一种方法。
编译打包后的 Knife4j 的 doc.html
文件位于 knife4j-openapi3-ui
包下面,它引入了两个文件 webjars/js/app.51033393.js
和 webjars/js/chunk-vendors.d51cf6f8.js
,它们都位于 knife4j-openapi3-ui
包下面。Vue 编译打包后的方法名不会变更,从上面的分析我们知道可以在 analysisSpringDocOpenApiGroupSuccess()
方法解析分组信息时把 basePath
拼接在 url
的前面。在 webjars/js/app.51033393.js
文件中可以找到这个方法,我们把它手动格式化一下
1 | ze.prototype.analysisSpringDocOpenApiGroupSuccess=function(e){ |
参考 SwaggerBootstrapUi
构造方法中的做法,从浏览器地址中截取拼接 basePath
1 | const path = window.location.pathname; |
把它们加入 analysisSpringDocOpenApiGroupSuccess()
方法中,然后修改 url
和 location
属性的赋值,加上 basePath
前缀
1 | ze.prototype.analysisSpringDocOpenApiGroupSuccess=function(e){ |
然后把修改后的文件按照 webjar 的方式放到自己项目的目录中,即自己项目的 resources/webjars/js
目录下,重启项目后就可以正常打开 Knife4j 的文档页面的,问题得到解决。