若依前后端分离项目Swagger接口文档配置与避坑指南

发布时间:2026/9/18 1:19:20
若依前后端分离项目Swagger接口文档配置与避坑指南 用若依做过前后端分离项目的朋友应该对swagger-ui.html这个页面都有印象。打开它就是一份能直接在线调用的接口文档没打开过的人第一次面对若依这一整套SpringBootVue工程时往往会觉得无从下手。前端同事问“登录接口参数是什么”测试问“这个字段到底传不传”后端自己写文档又经常忘了更新——这大概是每个项目组都经历过的狼狈。今天这篇就专门聊若依RuoYi里的Swagger接口文档它怎么自动生成、在哪里改、实际开发中怎么用起来更顺畅最后再把那些年踩过的坑一并晒出来。1. 先搞清楚若依和Swagger是怎么配合的1.1 没有接口文档的日子前后端协作有多痛先回忆一个场景后端把用户列表接口写好了前端来问“返回值里total是总数还是总页数”后端只好打开代码现场翻。翻完发现在TableDataInfo里还得解释一句“分页参数、排序参数都是若依封装好的你不需要传”。下一周接口改了个字段名文档没同步更新前端联调又出问题。在没有Swagger这类工具之前接口说明通常靠人工维护要么写一份Word或者Markdown文档放群里更新靠自觉基本几周后就没人看了要么前端直接读后端代码效率低不说对业务接口不熟悉的人很容易被BaseController里那些封装方法绕晕联调过程中最怕“文档说的”和“代码跑的”不一致最后只能当面拉着后端对着代码捋。Swagger解决的核心问题就是把接口说明和代码放在一起。你用注解在代码里写清楚“这个接口是干什么的、参数是什么、返回值长什么样”启动项目后它自动生成一份在线文档。代码改了文档跟着变不会出现“文档已经过期”这种争议。1.2 若依替你封装好的三层东西很多新手第一次看若依源码容易被ruoyi-framework、ruoyi-system、ruoyi-admin这些模块搞晕。如果只看Swagger相关的东西其实若依只做三件事。第一件事依赖管理。若依的ruoyi-framework/pom.xml里已经放好了springfox-swagger2和springfox-swagger-ui版本是2.9.2。这意味着你在自己的业务模块里写Controller不需要再单独引入Swagger依赖直接写注解就能被扫描到。第二件事自动扫描。项目里有一个SwaggerConfig配置类默认扫描com.ruoyi这个根包。只要Controller在这个包路径下启动后就会自动出现在文档里不需要一个个手工注册。第三件事权限放行。若依集成了Spring Security如果不做处理Swagger页面会被安全拦截器挡住。若依在SecurityConfig里已经对/swagger-ui.html、/swagger-resources/**、/webjars/**、/*/api-docs这几个路径做了匿名放行所以开发环境下打开文档页是不需要登录的。注意正是因为这个“匿名放行”生产环境直接部署到公网的时候Swagger接口文档是裸奔状态。这个问题后面单独讲。1.3 摸清Swagger工作原理它就是个“带说明书的接口清单”Swagger的工作原理通俗点说就是三个环节应用启动时框架扫描所有带RestController和Api注解的类然后把控制器方法上的GetMapping、PostMapping、ApiOperation等注解解析成一个“接口描述对象”最后通过/v2/api-docs这个JSON接口把描述暴露出去swagger-ui.html页面再把这个JSON渲染成可视化界面。所以你在页面上看到的“接口名、路径、参数、返回结果”本质上就是后端注解的翻译。注解写得好文档就清楚注解漏写了文档里就是一堆空的字段名。这一点想明白之后后面很多问题排查起来就简单了。另外Swagger页面里的“Try it out”功能可以直接发起真实请求。这对联调帮助很大后端不用开Postman前端不用急着搭页面直接在文档页里就能验证接口通不通。不过若依的接口大多有权限校验直接在文档里点“Execute”会返回401或403这个问题在第4章讲鉴权接入时一起解决。2. 从零跑通让若依的Swagger文档真正出现在浏览器里2.1 环境准备JDK、Maven、MySQL、Redis如果你是从零开始拉若依跑起来看Swagger先把环境核对一遍组件推荐版本说明JDK1.8 或 11若依官方要求JDK1.8我习惯用1.8稳定Maven3.6以上建议配好阿里云镜像不然拉依赖能等半天MySQL5.7 / 8.0需要本地装一个建库后导入sql脚本Redis5.x / 6.x若依Vue的验证码、部分缓存依赖Redis必须先启起来IDEIntelliJ IDEA社区版就能用不必非要旗舰版有几点要提醒新人Maven镜像务必配好国内直接拉中央仓库的依赖真的很痛苦Redis启动后默认没有密码若依默认配置就是不用密码如果你改了Redis密码记得同步改application.yml里的spring.redis.passwordMySQL字符集建议用utf8mb4避免导入脚本时出现中文乱码。2.2 拉代码、建库、改配置、启服务我以若依前后端分离版RuoYi-Vue为例操作步骤如下。第一步拉代码。在Gitee上找到RuoYi-Vue执行命令git clone https://gitee.com/y_project/RuoYi-Vue.git第二步建库导数据。在MySQL里创建数据库名字随意官方推荐ry-vue然后导入项目根目录下的sql/ry_2024xxxx.sql。导入命令不带图形界面也很快mysql -u root -p ry-vue ry_2024xxxx.sql如果你用Navicat这类工具直接“运行SQL文件”也完全可以。导入后重点看一下sys_user表里面默认有个admin用户密码存在sys_user里但通常是加密过的不用管登录接口会帮你校验。第三步改数据源配置。打开ruoyi-admin/src/main/resources/application-druid.yml修改这几项spring: datasource: druid: master: url: jdbc:mysql://localhost:3306/ry-vue?useUnicodetruecharacterEncodingutf8zeroDateTimeBehaviorconvertToNulluseSSLtrueserverTimezoneGMT%2B8 username: root password: 你自己的数据库密码第四步启动Redis。Windows下直接双击redis-server.exeMac或Linux用命令行启动。启动后确认端口6379能被访问。第五步启动后端。在IDEA中打开项目等Maven把依赖拉全后找到RuoYiApplication直接运行main方法。看到类似下面的日志就代表启动成功Started RuoYiApplication in 12.34 seconds如果你习惯命令行启动也可以这样cd ruoyi-admin mvn spring-boot:run我建议第一次还是用IDEA跑因为Maven命令行如果镜像没配好失败信息对新手不够直观。2.3 打开 swagger-ui.html 后先看这四个区域后端启动成功后浏览器访问http://localhost:8080/swagger-ui.html如果一切正常你会看到一个白底蓝边的页面。第一次打开建议先看四个区域心里有个底。顶部区域是文档标题和描述对应SwaggerConfig里的apiInfo默认显示“若依管理系统”。左上角有swagger-resources的下拉框单模块项目通常只有一个分组微服务项目里会出现多个分组的切换。左侧列表是接口清单按Controller分成了若干组比如“系统管理-用户管理”“系统管理-角色管理”“监控-在线用户”等。这是Api(tags 用户管理)里的tags决定的tags写得好左侧分组就一目了然。中间区域是选中接口的详情包括请求方式GET、POST等、请求路径、参数列表、返回类型。重点是参数区域如果Controller方法参数是一个实体对象页面会把实体里的每个字段都列出来字段名的注释就是实体类上的ApiModelProperty。右上角有一个 “Authorize / 全局参数” 入口若依默认可能没配置后面第4章会讲怎么加上Token鉴权。还有一点要记住swagger-ui.html页面本身可以匿名打开但你在页面里点开任意一个业务接口去“Try it out”大概率会返回“认证失败无法访问系统资源”这是若依的权限拦截在起作用属于正常现象不是文档坏了。3. 让接口文档“开口说话”注解体系与配置解析3.1 控制器层的三个注解照着抄就行接口文档想生成得漂亮Controller层这三个注解不能少Api加在Controller类上相当于给这个控制器起了个“分组名”。需要写在原注解直接复制Api(value 用户信息管理, tags {用户系统-用户管理}) RestController RequestMapping(/system/user) public class SysUserController extends BaseController { // ... }ApiOperation加在方法上是给具体接口写说明。它在文档里展示为接口名称比如“获取用户列表”“新增用户”ApiOperation(获取用户列表) PreAuthorize(ss.hasPermi(system:user:list)) GetMapping(/list) public TableDataInfo list(SysUser user) { startPage(); ListSysUser list userService.selectUserList(user); return getDataTable(list); }ApiImplicitParams和ApiImplicitParam用来描述那些零散参数尤其是单个参数不是实体对象的时候ApiOperation(删除用户) ApiImplicitParams({ ApiImplicitParam(name userIds, value 用户ID数组, required true, dataType Long[]) }) DeleteMapping(/{userIds}) public AjaxResult remove(PathVariable Long[] userIds) { return toAjax(userService.deleteUserByIds(userIds)); }说句实在话这三个注解是接口文档的骨架。我在实际项目里给团队定的规矩是新写一个Controller先加Api和ApiOperation参数如果字段超过三个必须补ApiImplicitParam。倒不是为了应付检查而是省得后面接口多了再去翻代码回忆。3.2 实体类上的说明注解前端最依赖的就是它如果说Controller注解决定了接口文档的“骨架”那实体类上的ApiModel和ApiModelProperty就决定了文档的“血肉”。以若依用户实体SysUser为例ApiModel(value 用户对象, description 系统用户实体) public class SysUser extends BaseEntity { private static final long serialVersionUID 1L; ApiModelProperty(用户ID) private Long userId; ApiModelProperty(部门ID) private Long deptId; ApiModelProperty(用户账号) private String userName; ApiModelProperty(用户昵称) private String nickName; ApiModelProperty(用户邮箱) private String email; ApiModelProperty(手机号码) private String phonenumber; ApiModelProperty(用户性别0男 1女 2未知) private String sex; // ... }前端同事看接口文档时最关心的就是“这个字段是干什么的、传什么格式”。如果你在实体类上不写ApiModelPropertySwagger页面里字段名就会光秃秃地躺在那里比如userId、deptId一眼看去根本不知道是什么。写了说明之后前端能直接照着字段名对接至少省去一轮线下沟通。这里有一个很容易忽略的细节若依的BaseEntity里还有searchValue、createBy、createTime、remark等字段它们也会出现在接口文档里。很多人第一次看到字段列表里的params、beginTime、endTime会觉得莫名其妙其实这是若依框架统一封装的查询条件。前端不需要每个都传你只要记得pageNum和pageSize这两个分页参数是由若依的startPage()自动接收的就行。3.3 SwaggerConfig改了这两个地方文档风格就变了若依的Swagger总配置类在ruoyi-framework/src/main/java/com/ruoyi/framework/config/SwaggerConfig.java核心是一个DocketBean。默认配置大致长这样Configuration EnableSwagger2 public class SwaggerConfig { Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage(com.ruoyi)) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title(若依管理系统) .description(若依管理系统接口文档) .termsOfServiceUrl(http://www.ruoyi.vip) .version(3.8.7) .build(); } }这里有两个地方你需要重点关注。一个是basePackage(com.ruoyi)。你的业务代码如果不在这个包下接口不会出现在文档里。比如你把业务模块写成了com.mycompany.project那必须把这行改成自己的包名。这也是“接口列表空白”最常见的原因之一。另一个是apiInfo()里的title和description。我见过很多团队直接把title改成项目名、description改成一段接口规范说明这样前端打开文档页第一眼就知道是哪个项目的文档减少误用环境的情况。多环境部署时还可以在description里加上当前环境的标识测试环境/生产环境全靠这几个字段。另外Springfox 本身支持多Docket分组比如按业务域拆分Bean public Docket userApi() { return new Docket(DocumentationType.SWAGGER_2) .groupName(用户模块) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage(com.ruoyi.web.controller.system)) .build(); }不过说实话若依单模块项目里默认一个Docket就够用了强行拆组反而增加维护成本。微服务架构下多分组才有实际意义这个在第4章展开。4. 进阶玩法鉴权接入、微服务聚合与安全防护4.1 把当前用户Token自动带进Swagger请求若依Vue版的前端登录后会把一个叫Admin-Token的请求头带给后端。Swagger文档页里直接用接口时这个请求头默认是不带的所以点“Execute”大概率会撞上权限校验。解决思路有两种。第一种是手工在接口请求里加Header每个接口都去填一遍非常麻烦。第二种是在Swagger的Docket配置里注册一个全局Header参数让文档里每个请求都自动带上Authorization。Springfox 2.9.2 的写法是用globalOperationParametersBean public Docket createRestApi() { ParameterBuilder tokenPar new ParameterBuilder(); ListParameter pars new ArrayList(); tokenPar.name(Authorization) .description(请求令牌) .modelRef(new ModelRef(string)) .parameterType(header) .required(false) .build(); pars.add(tokenPar.build()); return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage(com.ruoyi)) .paths(PathSelectors.any()) .build() .globalOperationParameters(pars); }注意如果你用的是RuoYi-Vue请求头名字通常是Authorization值是Bearer xxxxx或者直接token具体要看后端过滤器取的Header是哪个。若依源码里用的是SecurityUtils.getAuthentication()默认取Authorization你把SecurityConstants.TOKEN_HEADER这个常量的值看一遍就清楚了。配置好后你在Swagger页面里填入一个真实Token再点“Try it out”所有接口都能正常调通。这在前端页面还没开发完、后端需要自测接口时特别实用。4.2 RuoYi-Cloud 下多服务接口文档怎么组织若依微服务版 RuoYi-Cloud 比单机版要复杂一些每个服务模块ruoyi-system、ruoyi-job、ruoyi-file等都有自己的Swagger配置和独立的swagger-ui.html地址。常见做法是给每个微服务模块单独开启Swagger访问时分别打开对应服务的端口http://localhost:9201/swagger-ui.html # 认证中心 http://localhost:9202/swagger-ui.html # 系统模块 http://localhost:9203/swagger-ui.html # 定时任务但这在联调时有点痛苦前端要记住好几个地址。所以更推荐用网关聚合在网关模块ruoyi-gateway里把各个服务的/v2/api-docs聚合到一个Swagger界面上。具体方案可以借助已有的依赖或者在前端维护一个跳转菜单。如果你是新项目从零搭微服务其实可以直接考虑用SpringDoc springdoc-gateway 的聚合能力省去很多Springfox时代的hack配置。如果你的团队正打算从单机改造微服务我建议先别急着把Swagger升级先把服务划分清楚再想着聚合文档。微服务下的Swagger聚合本质上是路由层面的活儿服务没拆好文档聚起来也只会更乱。4.3 聊一聊 swagger api 未授权访问漏洞“swagger api 未授权访问漏洞【原理扫描】【可验证】”这个话题在安全审计报告里出现频率很高。原理很简单Swagger启动后会把所有接口的路径、参数、请求方法甚至请求体结构通过/v2/api-docs这个JSON接口完整暴露出来。攻击者根本不需要看你的前端代码直接访问这个JSON就能快速梳理出整个系统的攻击面然后挨个接口试未授权操作。若依默认开发环境是放开Swagger的问题不大。但如果你把项目部署到公网或者放在客户现场还是直接开着swagger-ui.html就等于把系统API目录免费送人。我自己常用的防护方案有三种按优先级排列。第一种开关控制。在配置里加上一个开关字段生产环境直接关闭swagger: enabled: false然后改造SwaggerConfig读取开关后决定是否创建DocketBeanConfiguration EnableSwagger2 public class SwaggerConfig { Value(${swagger.enabled:true}) private boolean enabled; Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage(com.ruoyi)) .paths(PathSelectors.any()) .build(); } }当然如果只是一味地在createRestApi()里 return 一个空Docket页面还是会初始化更好的做法是通过ConditionalOnProperty在配置层面直接放行不同公司写法不同核心思路就是“生产环境不放行”。第二种改路径。把默认的/swagger-ui.html和/v2/api-docs路径改成一段不容易猜到的路径。但说实话这只能防小白扫描器仍然可能探测到常见路径属于“低调处理”而不是根治。第三种网络隔离。生产环境Swagger只能在内网访问外网流量不进来。这是最彻底的方式一般配合网关或安全组实现。提醒一下如果安全扫描扫出来的是“可验证”的未授权访问说明对方已经能直接打开文档页或获取api-docs JSON了。别拖当天就该处理。5. 避坑实录从启动报错到文档空白5.1 SpringBoot 2.6启动就报NullPointerException这是若依升级过程中最经典的一个坑。Springfox 2.9.2 和 Spring Boot 2.6 以上版本存在路径匹配策略不兼容的问题。启动报错的核心信息一般是java.lang.NullPointerException: null at springfox.documentation.spring.web.WebMvcPatternsRequestConditionWrapper.getPatterns原因是Spring Boot 2.6默认使用PathPatternParser而Springfox还在用AntPathMatcher。解决办法是在application.yml里显式切回旧的匹配策略spring: mvc: pathmatch: matching-strategy: ant_path_matcher这个配置对若依老项目升级到Spring Boot 2.6 尤其重要。如果你用新版本若依本身没这问题但自己升过Boot版本建议第一件事就查这个。5.2 接口列表空白最容易被忽略的三个原因文档页面能打开但左侧一个接口都看不到99%的原因是这三个。第一个是包扫描路径不对。SwaggerConfig里basePackage(com.ruoyi)你的Controller不在com.ruoyi或子包下就不会被扫描到。解决办法是改成实际Controller所在包名。第二个是缺少Api注解或者类上的注解写成了ApiIgnore。若依的Controller大多标记了Api但你自己新建的一个测试Controller可能只写了RestController。没有Api的类Springfox默认不收录可以全局配置里调整但最简单的还是老老实实补上注解。第三个是spring.mvc.pathmatch.matching-strategy配置引起的问题如果在Spring Boot 2.6 上没设置ant_path_matcher接口列表也可能渲染不全。这种问题表面看是“列表空白”实际是启动时已经报错了只是开发模式吞掉了部分异常。先启动日志确认没有异常再考虑注解问题。5.3 参数描述全是空的原因在实体类上页面里接口能显示但参数列表里每个字段都没有注释看半天也不知道传什么。这种情况十有八九是实体类没加ApiModelProperty。如果你是直接从数据库表生成的实体类若依代码生成器默认会带上这个注解。但如果你自己手写了实体或者用了MyBatis逆向工具生成的文件可能没有Swagger注解。补一遍虽然繁琐但补完之后文档质量立刻上一个台阶。还有一个容易踩的细节当Controller方法里的参数是实体对象时如果写了RequestBodySwagger展示的是JSON形式请求体如果没写默认按表单参数展示。两种方式前端对接时的用法完全不同。建议接口设计时统一风格新增/修改类接口用RequestBody走JSON查询类接口用GET实体参数走query。混着用的话文档虽然能生成前端会疯。5.4 从 Springfox 平滑迁移到 SpringDoc 的方案很多人可能已经发现了Springfox 2.9.2 是2018年左右的老版本了之后就很少更新对新版Spring Boot的兼容性一直不好。如果你打算在新项目里用若依的思想重写或者想把老项目的Swagger升级一把我更推荐直接用SpringDoc。迁移成本其实不大。依赖替换dependency groupIdorg.springdoc/groupId artifactIdspringdoc-openapi-starter-webmvc-ui/artifactId version2.2.0/version /dependency访问地址从/swagger-ui.html变成/swagger-ui/index.html接口JSON从/v2/api-docs变成/v3/api-docs。注解方面Api对应TagApiOperation对应OperationApiModelProperty对应Schema。如果你不想改代码也可以暂时沿用旧注解SpringDoc部分兼容Springfox注解但长期还是建议统一到新注解上。在若依里替换时还有两个地方要同步改。一是SwaggerConfig需要整体重写二是SecurityConfig里的放行路径要新增/v3/api-docs/**和/swagger-ui/**。从经验看迁移SpringDoc后最常踩的坑就是访问页面出现404基本都在放行路径上。我个人在实际操作中的体会是Swagger这东西价值不在“有没有”而在“写得细不细”。若依本身就帮你把依赖、扫描、权限放行都配好了你真正要花时间的是给每个接口写清楚ApiOperation、给每个字段补上ApiModelProperty。有些团队嫌弃Swagger页面丑其实页面丑不丑不太重要前端能一眼看懂参数含义、后端能直接在线测接口联调效率提升是实打实的。最后再分享一个小技巧多环境部署的时候给每个环境的 Swagger 页面加一个环境标识比如测试环境在description里写“测试环境数据谨慎操作”生产环境直接关闭。这个小细节能避免很多人把测试环境的文档当成生产环境对着错误环境联调半天的尴尬。