rdms-spring-boot-starter-web
1. 模块定位
rdms-spring-boot-starter-web 是 Web 层基础设施模块,用来统一处理 HTTP 接口开发中的通用问题,而不是承载具体业务逻辑。
模块当前聚合了以下能力:
- Web 基础自动配置
- 全局异常处理
- 接口返回结果暂存
- 管理端 / 应用端接口前缀约定
- Swagger / Knife4j 文档
- API 访问日志
- XSS 防护
- API 加解密
- 返回字段脱敏
- Jackson JSON 定制
- Banner 输出
2. 设计思路
这个模块的设计重点,是把 Web 层横切能力收敛到 starter 中,业务模块只关注 Controller、参数对象和业务逻辑本身。
核心思路如下:
- 通过自动装配统一注册
Filter、ControllerAdvice、RestTemplate、Swagger 等基础组件。 - 通过包路径约定,自动给 Controller 增加统一前缀,减少每个模块重复写公共路径。
- 通过全局异常处理,把常见异常统一翻译成
CommonResult。 - 通过过滤器和拦截器处理访问日志、XSS、防重复读取请求体、演示环境保护等横切逻辑。
- 通过注解方式扩展局部能力,例如 API 访问日志增强、接口加解密、字段脱敏。
自动装配入口如下:
com.njcn.rdms.framework.apilog.config.RdmsApiLogAutoConfigurationcom.njcn.rdms.framework.jackson.config.RdmsJacksonAutoConfigurationcom.njcn.rdms.framework.swagger.config.RdmsSwaggerAutoConfigurationcom.njcn.rdms.framework.web.config.RdmsWebAutoConfigurationcom.njcn.rdms.framework.apilog.config.RdmsApiLogRpcAutoConfigurationcom.njcn.rdms.framework.xss.config.RdmsXssAutoConfigurationcom.njcn.rdms.framework.banner.config.RdmsBannerAutoConfigurationcom.njcn.rdms.framework.encrypt.config.RdmsApiEncryptAutoConfiguration
3. 功能模块
3.1 Web 基础自动配置
RdmsWebAutoConfiguration 是本模块的核心入口,主要提供以下能力:
- 注册全局异常处理器
GlobalExceptionHandler - 注册返回结果处理器
GlobalResponseBodyHandler - 注册
WebFrameworkUtils - 注册跨域过滤器
CorsFilter - 注册请求体缓存过滤器
CacheRequestBodyFilter - 提供普通
RestTemplate - 提供带
@LoadBalanced的RestTemplate
其中有两个基础能力需要重点关注:
3.1.1 按包路径自动增加接口前缀
模块会根据 Controller 所在包路径,自动追加统一前缀:
**.controller.admin.**默认追加/admin-api**.controller.app.**默认追加/app-api
默认配置来自 rdms.web:
rdms:
web:
admin-api:
prefix: /admin-api
controller: "**.controller.admin.**"
app-api:
prefix: /app-api
controller: "**.controller.app.**"
admin-ui:
url: http://127.0.0.1:80
# 说明:
# - admin-api/app-api 已有默认值(如上所示),不改可省略
# - admin-ui.url 无默认值,需要显式配置
例如:
package com.njcn.rdms.module.system.controller.admin.user;
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/get")
public CommonResult<String> get() {
return CommonResult.success("ok");
}
}
实际访问路径是:
/admin-api/user/get
如果 Controller 放在:
com.xxx.xxx.controller.app.xxx
则会自动挂到 /app-api 下。
3.1.2 CommonResult 不是自动包装
这个模块不会把任意 Controller 返回值自动包成 CommonResult。
GlobalResponseBodyHandler 的作用,是在返回前记录已经构造好的 CommonResult,供访问日志等能力读取,而不是替开发者自动改写返回结构。
因此 Controller 仍然需要显式返回:
return CommonResult.success(data);
而不是依赖框架自动包装。
3.2 全局异常处理
GlobalExceptionHandler 会把常见异常统一转换成 CommonResult,包括:
- 参数缺失
- 参数类型错误
@Validated/@Valid校验失败- 请求方式不匹配
- Content-Type 不匹配
- 无权限访问
- 业务异常
ServiceException - 系统异常
对于系统异常,除了统一返回错误结果外,还会通过 ApiErrorLogCommonApi 异步记录异常日志。
这意味着业务代码中通常只需要:
- 正常场景返回
CommonResult.success(...) - 业务错误抛出
ServiceException
其余异常由框架统一兜底。
3.3 接口返回结果暂存
这里的“接口返回结果记录”,更准确地说是“接口返回结果暂存”。
对应实现是 GlobalResponseBodyHandler,它只会在 Controller 返回值为 CommonResult 时,把该结果放到当前请求上下文中,供后续组件读取。
它本身不负责:
- 打印日志
- 持久化日志
- 改写返回结构
它主要服务于访问日志能力。因为 ApiAccessLogFilter 执行时,需要拿到本次请求最终返回的 CommonResult,才能把返回码、返回消息、响应体等内容写入访问日志。
可以这样理解两者关系:
- 接口返回结果暂存:把本次返回结果放到 request 上,供后续读取
- API 访问日志:读取请求信息和返回结果,组装后异步上报日志
因此,这两个能力不是并列重复关系,而是前者为后者提供支撑。
3.4 请求体缓存
CacheRequestBodyFilter 会把请求体包装成可重复读取的形式。
这个能力主要解决以下问题:
- 过滤器里读过一次请求体后,后续代码还能继续读取
- 访问日志、XSS、防护类过滤器可以提前读取请求内容
- 异常日志记录时可以再次拿到请求参数
这类能力对 application/json 请求尤其重要。
3.5 跨域处理
模块默认注册了全局 CorsFilter,允许:
- 任意来源
- 任意请求头
- 任意请求方法
- 携带凭证
适合前后端分离场景的统一跨域处理。
如果项目已经有更严格的网关级跨域策略,需要注意是否与这里的配置重复。
3.6 RestTemplate 支持
模块提供两个 RestTemplate Bean:
- 普通
RestTemplate - 支持服务名负载均衡的
loadBalancedRestTemplate
使用示例:
@Resource
private RestTemplate restTemplate;
@Resource(name = "loadBalancedRestTemplate")
private RestTemplate loadBalancedRestTemplate;
适用场景:
restTemplate:直接调用固定地址loadBalancedRestTemplate:通过服务名访问注册中心中的其他服务
3.7 Swagger / Knife4j 文档
模块会自动装配 OpenAPI,并按管理端、应用端拆分分组:
/admin-api/**/app-api/**
同时会在文档中预置常见请求头,例如认证头、租户头,便于在 Swagger 页面直接调试接口。
Swagger 配置示例:
rdms:
swagger:
title: RDMS API # 文档标题
description: RDMS 接口文档 # 文档描述
author: RDMS # 作者/团队
version: 1.0.0 # 版本号
url: https://example.com # 项目或团队主页
email: dev@example.com # 联系邮箱
license: Apache 2.0 # 协议名称
license-url: https://www.apache.org/licenses/LICENSE-2.0.html # 协议地址
开发时通常只需要:
- 引入本模块
- 补齐
rdms.swagger配置 - 在 Controller 上使用
@Tag - 在接口上使用
@Operation
这样访问日志模块还能直接复用这些注解信息,自动推断操作模块、操作名称。
3.8 API 访问日志
模块通过 ApiAccessLogFilter + ApiAccessLogInterceptor 组合处理接口访问日志。
这里的访问日志,才是真正意义上的“日志记录能力”。
处理逻辑分成两层:
- 拦截器层:把
HandlerMethod放到 request 中,供过滤器层读取 - 过滤器层:在请求结束后读取请求信息、异常信息、返回结果,并异步上报访问日志
访问日志内容包括:
- 用户信息
- 请求路径、方法、IP、User-Agent
- 请求参数
- 返回码、返回信息
- 执行耗时
- 操作模块、操作名称、操作类型
其中“返回码、返回信息、响应体”这部分数据,正是从前面的 GlobalResponseBodyHandler 暂存结果中读取出来的。
默认会记录访问日志;如需关闭,可配置:
rdms:
access-log:
enable: false
如果需要对某个接口单独调整日志行为,可使用 @ApiAccessLog。
常见用法示例:
@Tag(name = "用户管理")
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/page")
@Operation(summary = "查询用户分页")
@ApiAccessLog(responseEnable = true, sanitizeKeys = {"mobile"})
public CommonResult<PageResult<UserRespVO>> page(UserPageReqVO reqVO) {
return CommonResult.success(userService.page(reqVO));
}
}
说明:
responseEnable = true:额外记录响应体sanitizeKeys:从日志中移除敏感字段- 如果没有显式指定操作名称,默认优先从
@Operation中获取
访问日志的异步落库依赖 ApiAccessLogCommonApi,因此它本质上是“记录请求轨迹”,不是直接把日志写在本模块内部。
3.9 XSS 防护
模块内提供了 XSS 过滤能力,核心组成包括:
XssFilterXssRequestWrapperXssStringJsonDeserializerJsoupXssCleaner
设计方式是:
- 对进入系统的请求内容做统一清洗
- 对 JSON 中的字符串字段做清洗
- 底层使用
jsoup处理潜在的恶意脚本片段
适用场景:
- 富文本之外的大多数普通文本输入
- 表单提交
- JSON 请求体中的字符串字段
如果某些接口需要保留原始 HTML 内容,通常需要结合该模块的 XSS 配置做排除,而不是在业务层手工规避。
URL 白名单说明:
- 支持通过
rdms.xss.exclude-urls配置 URL 白名单,命中的 URL 会跳过 XSS 过滤(包括 Filter 与 JSON 字符串反序列化)。 - 适合富文本、HTML 片段等需要保留原始内容的接口。
示例:
rdms:
xss:
enable: true
exclude-urls:
- /admin-api/rich-text/**
- /app-api/article/save
3.10 API 加解密
模块提供了接口级加解密能力,核心组成包括:
@ApiEncryptApiEncryptFilterApiDecryptRequestWrapperApiEncryptResponseWrapper
适合理解为:
- 请求进入时先解密
- Controller / Service 内部仍然处理明文
- 响应返回前再加密
开发使用上,一般是在需要加解密的接口上增加 @ApiEncrypt,其余请求不受影响。
示例:
@RestController
@RequestMapping("/secure/demo")
public class SecureDemoController {
@PostMapping("/submit")
@ApiEncrypt
public CommonResult<String> submit(@RequestBody DemoReqVO reqVO) {
return CommonResult.success(reqVO.getContent());
}
}
这类能力适合:
- 对外开放接口
- 对传输内容有额外保护要求的场景
不适合把它当成通用权限控制手段。它解决的是“传输内容保护”,不是“访问授权”。
3.11 返回字段脱敏
模块内置了一组字段脱敏注解和序列化器,常见注解包括:
@MobileDesensitize@EmailDesensitize@IdCardDesensitize@BankCardDesensitize@ChineseNameDesensitize@PasswordDesensitize
适用方式是:在返回对象字段上直接加注解,序列化为 JSON 时自动脱敏。
示例:
@Data
public class UserRespVO {
private Long id;
@MobileDesensitize
private String mobile;
@EmailDesensitize
private String email;
@ChineseNameDesensitize
private String nickname;
}
这样接口内部仍可使用原始值,真正对外输出时才变成脱敏后的内容。
3.12 Jackson JSON 定制
模块包含 RdmsJacksonAutoConfiguration,用于统一注册 JSON 序列化 / 反序列化相关定制能力。
从模块结构上看,它主要承担两类职责:
- 承接本模块的 XSS、脱敏等 JSON 处理能力
- 把 JSON 相关行为统一收口到 starter 中,避免各业务模块重复配置
ObjectMapper
因此业务模块通常不需要自己再手动声明一套新的全局 Jackson 配置,除非有明确的覆盖需求。
3.13 Banner
模块还包含 RdmsBannerAutoConfiguration 和 BannerApplicationRunner,用于在应用启动时输出框架 Banner 信息。
这部分属于展示型能力,不影响具体业务逻辑。
4. 开发人员上手
4.1 引入依赖
<dependency>
<groupId>com.njcn</groupId>
<artifactId>rdms-spring-boot-starter-web</artifactId>
</dependency>
本模块已经聚合:
spring-boot-starter-webspring-boot-starter-validationknife4j-openapi3-jakarta-spring-boot-starterspringdoc-openapi-starter-webmvc-ui
如果项目已经单独引入了这些依赖,需要留意是否存在重复配置。
4.2 准备基础配置
建议至少配置以下内容:
spring:
application:
name: rdms-system-server
rdms:
web:
admin-api:
prefix: /admin-api
controller: "**.controller.admin.**"
app-api:
prefix: /app-api
controller: "**.controller.app.**"
admin-ui:
url: http://127.0.0.1:80
swagger:
title: RDMS API # 文档标题
description: RDMS 接口文档 # 文档描述
author: RDMS # 作者/团队
version: 1.0.0 # 版本号
url: https://example.com # 项目或团队主页
email: dev@example.com # 联系邮箱
license: Apache 2.0 # 协议名称
license-url: https://www.apache.org/licenses/LICENSE-2.0.html # 协议地址
access-log:
enable: true
4.3 按约定放置 Controller
如果希望自动带上前缀,需要把 Controller 放到约定包下:
- 管理端:
xx.controller.admin.xx - 应用端:
xx.controller.app.xx
不符合这个包路径规则的 Controller,不会自动追加 /admin-api 或 /app-api。
4.4 统一返回 CommonResult
Controller 写法建议保持统一:
@Tag(name = "部门管理")
@RestController
@RequestMapping("/dept")
public class DeptController {
@GetMapping("/get")
@Operation(summary = "获得部门详情")
public CommonResult<DeptRespVO> get(@RequestParam("id") Long id) {
return CommonResult.success(deptService.get(id));
}
}
建议遵循:
- 成功场景返回
CommonResult.success(...) - 业务异常抛出
ServiceException - 参数对象使用
@Validated/@Valid
4.5 使用访问日志
多数情况下,不需要额外写代码,访问日志会自动生效。
如果需要补充操作名、操作类型、响应体记录等信息,可以加:
@ApiAccessLog(responseEnable = true)
建议同时补齐:
@Tag@Operation
这样日志中的操作模块和操作名称会更完整;建议将 @Tag / @Operation 作为接口开发的必填规范。
4.6 使用加解密
只在需要的接口上加:
@ApiEncrypt
然后让调用方按约定传输密文即可。接口内部仍按明文对象开发,不需要在 Controller 内手工做解密逻辑。
4.7 使用字段脱敏
在返回对象字段上直接加注解即可:
@MobileDesensitize
private String mobile;
适合用户信息、联系方式、证件信息等对外展示场景。
4.8 使用 RestTemplate
固定地址调用:
@Resource
private RestTemplate restTemplate;
服务名调用:
@Resource(name = "loadBalancedRestTemplate")
private RestTemplate loadBalancedRestTemplate;
4.9 开发时需要注意的几个点
- 这个模块不会自动把任意返回值包装成
CommonResult,需要显式返回。 - 接口前缀是按包路径匹配出来的,不是看 Controller 名称。
- 访问日志默认开启,但是否真正异步落库,还取决于日志相关 RPC 接口是否可用。
- XSS、防脱敏、加解密都属于横切能力,原则上应该通过统一配置或注解使用,不建议在业务代码里重复造轮子。
- 如果项目已经在网关层处理跨域、日志、安全头等逻辑,需要评估是否与本模块职责重叠。
5. 适合承载什么,不适合承载什么
适合放在这个模块里的能力:
- Web 层公共配置
- HTTP 请求/响应横切处理
- 接口文档
- 访问日志
- 安全性输入输出处理
- Web 基础工具注册
不适合放在这个模块里的能力:
- 具体业务规则
- 菜单、权限、数据权限等业务权限判断
- 某个业务模块专属的 Controller 逻辑
- 与单一业务强耦合的转换规则
从职责上看,这个模块更接近“Web 基础设施层”,而不是“业务功能层”。