Gateway网关下Knife4j文档聚合,报错:SyntaxError: Unexpected token ‘<’, "<!DOCTYPE "… is not valid JSON
一、情景再现
二、原因分析
bz这里使用的是Sa-Token下的微服务 - 网关统一鉴权,报错原因在网上找了好久也没找到,最终问题发现在Sa-Token的全局过滤器上。
这是最开始的Sa-Token全局过滤器
@Configuration
public class SaTokenConfigure {
// 注册 Sa-Token全局过滤器
@Bean
public SaReactorFilter getSaReactorFilter() {
return new SaReactorFilter()
// 拦截地址
.addInclude("/**") /* 拦截全部path */
// 开放地址
.addExclude(
// Knife4j前端页面
"/doc.html",
// Swagger资源路径
"/webjars/**",
"/swagger-resources/**",
"/swagger-ui/**",
"/swagger-ui.html",
// 聚合的OpenAPI文档路径(关键!)
"/v2/api-docs",
"/v2/api-docs/**",
// 其他白名单
"/favicon.ico")
// 鉴权方法:每次访问进入
.setAuth(obj -> {
// 登录校验 -- 拦截所有路由,并排除/user/doLogin 用于开放登录
SaRouter.match("/**").notMatch(Constant.WHITE_LIST).check(r -> StpUtil.checkLogin());
})
// 异常处理方法:每次setAuth函数出现异常时进入
.setError(e -> {
return SaResult.error(e.getMessage());
});
}
}
访问swagger聚合文档(http://localhost:8080/doc.html)报错,以为是依赖问题,更换依赖版本后还是不行。(SpringBoot-2.7.12、JDK11)
<!--网关进行聚合的组件knife4j-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-gateway-spring-boot-starter</artifactId>
<version>4.5.0</version>
</dependency>
<!--其他模块knife4j-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
<version>4.5.0</version>
</dependency>
然后我把关注点放在了Sa-Token全局过滤器的异常处理那里。浏览器在报错,idea的控制台却没有报错,于是我把异常处理方法改成了项目的通用返回结果类R。
// 异常处理方法:每次setAuth函数出现异常时进入
.setError(e -> {
return R.error(e.getMessage());
});
再次访问聚合文档,还是报错。
但是与第一次报错有所区别,第一次是“Unexpected token ‘<’, "<!DOCTYPE "”,而这次却是“Unexpected token ‘R’, “R(code=100””。它们指的都是返回给浏览器不是JSON格式数据。于是我又把异常处理返回结果改为了自定义异常处理类。
.setError(e -> {
throw new CommonException(e.getMessage(),100500);
});
@Getter
public class CommonException extends RuntimeException{
private int code;
public CommonException(String message, int code) {
super(message);
this.code = code;
}
public CommonException(String message, Throwable cause, int code) {
super(message, cause);
this.code = code;
}
public CommonException(Throwable cause, int code) {
super(cause);
this.code = code;
}
}
再次访问文档,发现报错原因变成了这样,于是又在拦截路径那里添加放行路由"/v3/api-docs/**"。
再次访问文档,发现报错原因又变成了这样,于是又在拦截路径那里添加放行路由-微服务自定义的Swagger分组路径"/standardSwagger/**"。
再次访问文档,成功打开文档。
三、参考代码
修改后的Sa-Token全局过滤器
@Configuration
public class SaTokenConfigure {
// 注册 Sa-Token全局过滤器
@Bean
public SaReactorFilter getSaReactorFilter() {
return new SaReactorFilter()
// 拦截地址
.addInclude("/**") /* 拦截全部path */
// 开放地址
.addExclude(
// Knife4j前端页面
"/doc.html",
// Swagger资源路径
"/webjars/**",
"/swagger-resources/**",
"/swagger-ui/**",
"/swagger-ui.html",
// 聚合的OpenAPI文档路径(关键!)
"/v2/api-docs",
"/v2/api-docs/**",
"/v3/api-docs/**",
// 微服务自定义的Swagger分组路径
"/userSwagger/**",
"/standardSwagger/**",
// 其他白名单
"/favicon.ico")
// 鉴权方法:每次访问进入
.setAuth(obj -> {
// 登录校验 -- 拦截所有路由,并排除/user/doLogin 用于开放登录
SaRouter.match("/**").notMatch(Constant.WHITE_LIST).check(r -> StpUtil.checkLogin());
})
// 异常处理方法:每次setAuth函数出现异常时进入
.setError(e -> {
throw new CommonException(e.getMessage(),100500);
});
}
}
网关配置文件
server:
port: 8080
spring:
application:
name: gateway
mvc:
pathmatch:
matching-strategy: ant_path_matcher
cloud:
nacos:
server-addr: localhost:8848
gateway:
routes:
- id: standard-service # 路由规则id,自定义,唯一
uri: lb://standard-service # 路由的目标服务,lb代表负载均衡,会从注册中心拉取服务列表
predicates: # 路由断言,判断当前请求是否符合当前规则,符合则路由到目标服务
- Path=/codeTable/**,/data-standard/**,/test/**,/standardSwagger/** # 这里是以请求路径作为判断规则
filters:
# 仅对 /standard/** 路径重写,其他路径保持不变
- RewritePath=/standardSwagger(?<segment>/?.*), $\{segment}
- id: user-service
uri: lb://user-service
predicates:
- Path=/user/**,/userSwagger/**
filters:
- RewritePath=/userSwagger(?<segment>/?.*), $\{segment}
#knife4j的网关聚合配置 文档地址:http://{gateway.host}:{gateway.port}/doc.html
knife4j:
# # 聚合swagger文档
gateway:
enabled: true
# 排序规则(tag/operation排序自4.2.0版本新增)
# 取值:alpha-默认排序规则,官方swagger-ui默认实现,order-Knife4j提供的增强排序规则,开发者可扩展x-order,根据数值来自定义排序
tags-sorter: order
operations-sorter: order
# 指定手动配置的模式(默认为该模式)
strategy: manual
discover:
# 是否开启服务发现模式的配置
enabled: false
# 指定版本号(swagger2|openapi3)
version: swagger2
routes:
- name: 数据标准管理接口
# 真实子服务访问url地址-提供OpenAPI的文档
url: /standardSwagger/v2/api-docs?group=standardSwagger
# url: /v3/api-docs
service-name: standard-service
# 路由前缀
# 兼容OpenAPI3规范在聚合时丢失contextPath属性的异常情况,由开发者自己配置contextPath,Knife4j的前端Ui做兼容处理,与url属性独立不冲突,仅OpenAPI3规范聚合需要,OpenAPI2规范不需要设置此属性,默认为(apiPathPrefix)
context-path: /
order: 2
- name: 用户管理接口
#事实上我们也可以通过路由转发到其它单个服务上面去,从而达到聚合其它服务的说明文档,比如下面的云服务接口
url: /userSwagger/v2/api-docs?group=userSwagger
service-name: user-service
# 路由前缀
context-path: /
order: 3
子服务配置文件
knife4j:
enable: true
openapi:
title: 数据工厂(数据标准管理)服务
description: 数据工厂(数据标准管理)服务
email: 123456@gmail.com
concat: rq
url: https://docs.xiaominfo.com
version: v4.0
license: Apache 2.0
license-url: https://stackoverflow.com/
terms-of-service-url: https://stackoverflow.com/
group:
default:
group-name: standardSwagger
api-rule: package
api-rule-resources:
- com.cqie.standard.controller
nse-url: https://stackoverflow.com/
terms-of-service-url: https://stackoverflow.com/
group:
default:
group-name: standardSwagger
api-rule: package
api-rule-resources:
- com.cqie.standard.controller