外观
Web 层能力(nebula-framework-web)
2026-04-01
nebula-framework-web提供了业务 HTTP 接口所需的公共横切能力:统一响应包装、请求日志、全局异常处理、数据脱敏、防重提交和 CORS 配置。
引入nebula-boot-starter-web后,这些能力自动生效,业务代码无需额外配置。
目录
一、全局异常处理
GlobalExceptionHandler 统一捕获异常并转换为 R<Void> 响应,Controller 层不需要任何 try-catch。
1.1 处理规则
| 异常类型 | 响应 code | 说明 |
|---|---|---|
BusinessException | 业务错误码(如 400002) | 业务校验失败,返回友好提示 |
NebulaException (非Business) | 500 | 系统错误 |
MethodArgumentNotValidException | 400002 | @Valid 校验失败,自动提取第一条错误消息 |
BindException | 400002 | 表单绑定失败 |
ConstraintViolationException | 400002 | @Validated 方法级参数校验失败 |
HttpMessageNotReadableException | 400003 | 请求体解析失败(如 JSON 格式错误) |
MissingServletRequestParameterException | 400001 | 缺少必填请求参数 |
NoHandlerFoundException / NoResourceFoundException | 404 | 接口不存在 |
HttpRequestMethodNotSupportedException | 405 | 请求方法不支持 |
HttpMediaTypeNotSupportedException | 415 | Content-Type 不支持 |
UnsupportedOperationException | 503 | 服务不可用 |
Exception(兜底) | 500 | 未知系统错误 |
1.2 示例
Controller 只需按规范抛异常,无需手动 catch:
@PostMapping
public R<Long> createDemo(@Valid @RequestBody DemoSaveParamVO saveParam) {
// Service 内部会抛 BusinessException,这里无需处理
return R.ok(demoService.createDemo(saveParam));
}当参数校验失败时,框架自动返回:
{
"code": 400002,
"message": "示例名称不能为空",
"traceId": "2036344300517855232",
"data": null
}二、统一响应包装
ResponseResultHandler 对所有 Controller 返回值做自动包装:
- 返回值已是
R<T>类型 → 原样透传,不重复包装 - 返回值为裸对象 → 自动包装为
R.ok(data) - 接口标注了
@IgnoreResponseWrap→ 跳过包装(适用于文件下载、SSE 等特殊响应)
// 跳过自动包装(适用于文件下载等)
@IgnoreResponseWrap
@GetMapping("/{id}/download")
public void download(@PathVariable Long id, HttpServletResponse response) {
fileService.download(id, response);
}建议: 业务 Controller 仍然统一返回
R<T>,保持代码语义清晰,避免依赖自动包装行为。
三、请求日志过滤器
RequestLoggingFilter 自动记录每个 HTTP 请求与响应,便于问题排查。
3.1 日志内容
请求日志(日志级别 DEBUG):
- HTTP 方法、URI 路径
- 客户端 IP(支持从
X-Real-IP、X-Forwarded-For等代理头获取真实 IP) - Content-Type
- 请求 body(
multipart/和application/octet-stream类型跳过 body 打印)
响应日志(日志级别 DEBUG):
- URI 路径
- HTTP 状态码
- 请求耗时(毫秒)
- 响应 body 完整内容
3.2 日志输出示例
[REQUEST] POST /ltc/demo | IP: 192.168.1.100 | Content-Type: application/json
{"demoName":"示例数据","demoCode":"DEMO001"}
[RESPONSE] POST /ltc/demo | Status: 200 | Duration: 23ms
{"code":0,"message":"操作成功","traceId":"2036344300517855232","data":1001}3.3 配置
nebula:
web:
logging:
enabled: true # 默认 true,开发环境建议开启
order: -100 # Filter 执行顺序,默认 -100(优先于业务 Filter)四、数据脱敏
通过 @Desensitize 注解,在 JSON 序列化阶段自动对敏感字段做脱敏处理,不影响数据库存储值。
4.1 内置脱敏类型
SensitizeType | 说明 | 示例 |
|---|---|---|
PHONE | 手机号 | 138****8888 |
ID_CARD | 身份证 | 110101****1234 |
EMAIL | 邮箱 | a***@example.com |
NAME | 姓名 | 张* |
BANK_CARD | 银行卡 | 6222****8888 |
ADDRESS | 地址 | 上海市浦东新区**** |
PASSWORD | 密码 | ****** |
LICENSE_PLATE | 车牌 | 沪A****8 |
CUSTOM | 自定义(配合 prefixLen/suffixLen) | — |
4.2 使用方式
在响应 VO 的字段上添加 @Desensitize 注解:
@Data
@Schema(description = "用户列表项")
public class UserRespVO implements Serializable {
@Schema(description = "用户名")
private String username;
// 手机号脱敏:138****8888
@Desensitize(SensitizeType.PHONE)
@Schema(description = "手机号")
private String mobile;
// 身份证脱敏
@Desensitize(SensitizeType.ID_CARD)
@Schema(description = "身份证号")
private String idCard;
// 自定义:保留前2位和后4位,中间替换为 *
@Desensitize(value = SensitizeType.CUSTOM, prefixLen = 2, suffixLen = 4, maskChar = '*')
@Schema(description = "自定义敏感字段")
private String customField;
}注意: 脱敏只在序列化(输出 JSON)时生效,不影响 Java 对象中的原始值。如果需要在 Service 层获取原始值,直接读取字段即可。
五、防重提交
@RepeatSubmit 注解防止接口在短时间内被重复调用(如用户多次点击提交按钮)。
5.1 使用方式
@Operation(summary = "新增示例")
@RepeatSubmit(interval = 5, timeUnit = TimeUnit.SECONDS, message = "操作过于频繁,请稍后再试")
@PostMapping
public R<Long> createDemo(@Valid @RequestBody DemoSaveParamVO saveParam) {
return R.ok(demoService.createDemo(saveParam));
}5.2 参数说明
| 参数 | 默认值 | 说明 |
|---|---|---|
interval | 5 | 防重时间窗口 |
timeUnit | TimeUnit.SECONDS | 时间单位 |
message | "操作过于频繁" | 触发防重时的提示消息 |
keyExtra | "" | SpEL 表达式,附加到 key 中用于更细粒度区分(如按入参中某个字段) |
5.3 防重 Key 生成规则
框架使用以下信息生成指纹,并在 Redis 中加锁:
MD5(userId + method + URI + requestBodyHash + keyExtra)- 同一用户、同一接口、同一请求体的重复调用会被拦截
- 触发时抛出
BusinessException(GlobalErrorCode.REPEATED_REQUEST)
5.4 配置
nebula:
web:
repeat-submit:
enabled: true # 默认 true
interval: 5 # 全局默认防重窗口(秒),可被注解参数覆盖依赖:
@RepeatSubmit的切面实现在nebula-boot-starter-web中,需引入该 Starter 才能生效。底层存储依赖 Redis(nebula-boot-starter-redis)。
六、CORS 跨域配置
框架支持通过配置统一开启跨域,无需在每个 Controller 上添加 @CrossOrigin。
nebula:
web:
cors:
enabled: true
mapping: /**
allowed-origin-patterns:
- http://localhost:*
- https://*.yourdomain.com
allowed-methods:
- GET
- POST
- PUT
- DELETE
- PATCH
- OPTIONS
allowed-headers: "*"
allow-credentials: true
max-age: 3600七、配置参考
nebula.web 下的完整配置项:
nebula:
web:
# 响应自动包装
response-wrap:
enabled: true # 默认 true
# 跨域
cors:
enabled: false # 默认关闭,按需开启
mapping: /**
allowed-origin-patterns: []
allowed-methods: [GET, POST, PUT, DELETE, PATCH, OPTIONS]
allowed-headers: "*"
allow-credentials: true
max-age: 3600
# 请求日志
logging:
enabled: true # 默认 true
order: -100 # Filter 优先级
# 防重提交
repeat-submit:
enabled: true # 默认 true
interval: 5 # 默认防重时间窗口(秒)