607 lines
16 KiB
Markdown
607 lines
16 KiB
Markdown
|
|
# 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.RdmsApiLogAutoConfiguration`
|
|||
|
|
- `com.njcn.rdms.framework.jackson.config.RdmsJacksonAutoConfiguration`
|
|||
|
|
- `com.njcn.rdms.framework.swagger.config.RdmsSwaggerAutoConfiguration`
|
|||
|
|
- `com.njcn.rdms.framework.web.config.RdmsWebAutoConfiguration`
|
|||
|
|
- `com.njcn.rdms.framework.apilog.config.RdmsApiLogRpcAutoConfiguration`
|
|||
|
|
- `com.njcn.rdms.framework.xss.config.RdmsXssAutoConfiguration`
|
|||
|
|
- `com.njcn.rdms.framework.banner.config.RdmsBannerAutoConfiguration`
|
|||
|
|
- `com.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`:
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
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 无默认值,需要显式配置
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
例如:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
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");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
实际访问路径是:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
/admin-api/user/get
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
如果 Controller 放在:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
com.xxx.xxx.controller.app.xxx
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
则会自动挂到 `/app-api` 下。
|
|||
|
|
|
|||
|
|
#### 3.1.2 `CommonResult` 不是自动包装
|
|||
|
|
|
|||
|
|
这个模块不会把任意 Controller 返回值自动包成 `CommonResult`。
|
|||
|
|
|
|||
|
|
`GlobalResponseBodyHandler` 的作用,是在返回前记录已经构造好的 `CommonResult`,供访问日志等能力读取,而不是替开发者自动改写返回结构。
|
|||
|
|
|
|||
|
|
因此 Controller 仍然需要显式返回:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
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`
|
|||
|
|
|
|||
|
|
使用示例:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
@Resource
|
|||
|
|
private RestTemplate restTemplate;
|
|||
|
|
|
|||
|
|
@Resource(name = "loadBalancedRestTemplate")
|
|||
|
|
private RestTemplate loadBalancedRestTemplate;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
适用场景:
|
|||
|
|
|
|||
|
|
- `restTemplate`:直接调用固定地址
|
|||
|
|
- `loadBalancedRestTemplate`:通过服务名访问注册中心中的其他服务
|
|||
|
|
|
|||
|
|
### 3.7 Swagger / Knife4j 文档
|
|||
|
|
|
|||
|
|
模块会自动装配 OpenAPI,并按管理端、应用端拆分分组:
|
|||
|
|
|
|||
|
|
- `/admin-api/**`
|
|||
|
|
- `/app-api/**`
|
|||
|
|
|
|||
|
|
同时会在文档中预置常见请求头,例如认证头、租户头,便于在 Swagger 页面直接调试接口。
|
|||
|
|
|
|||
|
|
Swagger 配置示例:
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
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` 暂存结果中读取出来的。
|
|||
|
|
|
|||
|
|
默认会记录访问日志;如需关闭,可配置:
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
rdms:
|
|||
|
|
access-log:
|
|||
|
|
enable: false
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
如果需要对某个接口单独调整日志行为,可使用 `@ApiAccessLog`。
|
|||
|
|
|
|||
|
|
常见用法示例:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
@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 过滤能力,核心组成包括:
|
|||
|
|
|
|||
|
|
- `XssFilter`
|
|||
|
|
- `XssRequestWrapper`
|
|||
|
|
- `XssStringJsonDeserializer`
|
|||
|
|
- `JsoupXssCleaner`
|
|||
|
|
|
|||
|
|
设计方式是:
|
|||
|
|
|
|||
|
|
- 对进入系统的请求内容做统一清洗
|
|||
|
|
- 对 JSON 中的字符串字段做清洗
|
|||
|
|
- 底层使用 `jsoup` 处理潜在的恶意脚本片段
|
|||
|
|
|
|||
|
|
适用场景:
|
|||
|
|
|
|||
|
|
- 富文本之外的大多数普通文本输入
|
|||
|
|
- 表单提交
|
|||
|
|
- JSON 请求体中的字符串字段
|
|||
|
|
|
|||
|
|
如果某些接口需要保留原始 HTML 内容,通常需要结合该模块的 XSS 配置做排除,而不是在业务层手工规避。
|
|||
|
|
|
|||
|
|
URL 白名单说明:
|
|||
|
|
|
|||
|
|
- 支持通过 `rdms.xss.exclude-urls` 配置 URL 白名单,命中的 URL 会跳过 XSS 过滤(包括 Filter 与 JSON 字符串反序列化)。
|
|||
|
|
- 适合富文本、HTML 片段等需要保留原始内容的接口。
|
|||
|
|
|
|||
|
|
示例:
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
rdms:
|
|||
|
|
xss:
|
|||
|
|
enable: true
|
|||
|
|
exclude-urls:
|
|||
|
|
- /admin-api/rich-text/**
|
|||
|
|
- /app-api/article/save
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.10 API 加解密
|
|||
|
|
|
|||
|
|
模块提供了接口级加解密能力,核心组成包括:
|
|||
|
|
|
|||
|
|
- `@ApiEncrypt`
|
|||
|
|
- `ApiEncryptFilter`
|
|||
|
|
- `ApiDecryptRequestWrapper`
|
|||
|
|
- `ApiEncryptResponseWrapper`
|
|||
|
|
|
|||
|
|
适合理解为:
|
|||
|
|
|
|||
|
|
- 请求进入时先解密
|
|||
|
|
- Controller / Service 内部仍然处理明文
|
|||
|
|
- 响应返回前再加密
|
|||
|
|
|
|||
|
|
开发使用上,一般是在需要加解密的接口上增加 `@ApiEncrypt`,其余请求不受影响。
|
|||
|
|
|
|||
|
|
示例:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
@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 时自动脱敏。
|
|||
|
|
|
|||
|
|
示例:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
@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 引入依赖
|
|||
|
|
|
|||
|
|
```xml
|
|||
|
|
<dependency>
|
|||
|
|
<groupId>com.njcn</groupId>
|
|||
|
|
<artifactId>rdms-spring-boot-starter-web</artifactId>
|
|||
|
|
</dependency>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
本模块已经聚合:
|
|||
|
|
|
|||
|
|
- `spring-boot-starter-web`
|
|||
|
|
- `spring-boot-starter-validation`
|
|||
|
|
- `knife4j-openapi3-jakarta-spring-boot-starter`
|
|||
|
|
- `springdoc-openapi-starter-webmvc-ui`
|
|||
|
|
|
|||
|
|
如果项目已经单独引入了这些依赖,需要留意是否存在重复配置。
|
|||
|
|
|
|||
|
|
### 4.2 准备基础配置
|
|||
|
|
|
|||
|
|
建议至少配置以下内容:
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
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 写法建议保持统一:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
@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 使用访问日志
|
|||
|
|
|
|||
|
|
多数情况下,不需要额外写代码,访问日志会自动生效。
|
|||
|
|
|
|||
|
|
如果需要补充操作名、操作类型、响应体记录等信息,可以加:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
@ApiAccessLog(responseEnable = true)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
建议同时补齐:
|
|||
|
|
|
|||
|
|
- `@Tag`
|
|||
|
|
- `@Operation`
|
|||
|
|
|
|||
|
|
这样日志中的操作模块和操作名称会更完整;建议将 `@Tag` / `@Operation` 作为接口开发的必填规范。
|
|||
|
|
|
|||
|
|
### 4.6 使用加解密
|
|||
|
|
|
|||
|
|
只在需要的接口上加:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
@ApiEncrypt
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
然后让调用方按约定传输密文即可。接口内部仍按明文对象开发,不需要在 Controller 内手工做解密逻辑。
|
|||
|
|
|
|||
|
|
### 4.7 使用字段脱敏
|
|||
|
|
|
|||
|
|
在返回对象字段上直接加注解即可:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
@MobileDesensitize
|
|||
|
|
private String mobile;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
适合用户信息、联系方式、证件信息等对外展示场景。
|
|||
|
|
|
|||
|
|
### 4.8 使用 RestTemplate
|
|||
|
|
|
|||
|
|
固定地址调用:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
@Resource
|
|||
|
|
private RestTemplate restTemplate;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
服务名调用:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
@Resource(name = "loadBalancedRestTemplate")
|
|||
|
|
private RestTemplate loadBalancedRestTemplate;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4.9 开发时需要注意的几个点
|
|||
|
|
|
|||
|
|
- 这个模块不会自动把任意返回值包装成 `CommonResult`,需要显式返回。
|
|||
|
|
- 接口前缀是按包路径匹配出来的,不是看 Controller 名称。
|
|||
|
|
- 访问日志默认开启,但是否真正异步落库,还取决于日志相关 RPC 接口是否可用。
|
|||
|
|
- XSS、防脱敏、加解密都属于横切能力,原则上应该通过统一配置或注解使用,不建议在业务代码里重复造轮子。
|
|||
|
|
- 如果项目已经在网关层处理跨域、日志、安全头等逻辑,需要评估是否与本模块职责重叠。
|
|||
|
|
|
|||
|
|
## 5. 适合承载什么,不适合承载什么
|
|||
|
|
|
|||
|
|
适合放在这个模块里的能力:
|
|||
|
|
|
|||
|
|
- Web 层公共配置
|
|||
|
|
- HTTP 请求/响应横切处理
|
|||
|
|
- 接口文档
|
|||
|
|
- 访问日志
|
|||
|
|
- 安全性输入输出处理
|
|||
|
|
- Web 基础工具注册
|
|||
|
|
|
|||
|
|
不适合放在这个模块里的能力:
|
|||
|
|
|
|||
|
|
- 具体业务规则
|
|||
|
|
- 菜单、权限、数据权限等业务权限判断
|
|||
|
|
- 某个业务模块专属的 Controller 逻辑
|
|||
|
|
- 与单一业务强耦合的转换规则
|
|||
|
|
|
|||
|
|
从职责上看,这个模块更接近“Web 基础设施层”,而不是“业务功能层”。
|