From b6676fbb5fe0faf1a75b6e4f44e298ec6ccceeb3 Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Sat, 9 May 2026 15:19:21 +0800 Subject: [PATCH] =?UTF-8?q?docs(common):=20@Valid=20=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=20toast=20=E5=8E=BB=E6=8E=89=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E5=90=8D=E5=89=8D=E7=BC=80,=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E6=9B=B4=E5=8F=8B=E5=A5=BD=20(PR=20#1916)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hl-common-log 全局 GlobalExceptionHandler 改动,影响 6 个服务所有 @Valid + @RequestBody 接口的 400 校验响应文案。message 字符串从 "字段名: 文案" 变为 "仅文案"(多字段仍以 ;空格 分隔)。无前端改动情况下绝大多数 Toast(message) 直接更友好。提醒 mmg 全局搜 message.startsWith / split / match 等解析逻 辑确认无误。 --- ...ommon_validation_msg_strip_field_prefix.md | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 changelogs/2026-05/09_fix_common_validation_msg_strip_field_prefix.md diff --git a/changelogs/2026-05/09_fix_common_validation_msg_strip_field_prefix.md b/changelogs/2026-05/09_fix_common_validation_msg_strip_field_prefix.md new file mode 100644 index 0000000..f82b88a --- /dev/null +++ b/changelogs/2026-05/09_fix_common_validation_msg_strip_field_prefix.md @@ -0,0 +1,129 @@ +# @Valid 校验失败 toast 去掉字段名前缀,提示更友好 + +> **服务**: hl-common-log (共享 module,影响全部 6 个后端服务) +> **PR**: #1916 +> **Issue**: 无(从前端 BUG 通知 `09_frontend_notice_admin_service-edit-categorycode-required.md` 衍生的后端体验改善) +> **日期**: 2026-05-09 +> **影响范围**: 全部 admin / mp / 内部接口的 `@Valid + @RequestBody` 触发的 400 校验响应文案 + +--- + +## ⚠️ 关键变化 + +后端全局 `MethodArgumentNotValidException` / `BindException` 处理器返回的 `Result.message` 文案格式变了: + +| | 改前 | 改后 | +|---|---|---| +| **单字段错误** | `categoryCode: 分类编码不能为空` | `分类编码不能为空` | +| **多字段错误** | `name: 姓名不能为空; age: 必须为正数` | `姓名不能为空; 必须为正数` | +| **defaultMessage 为 null 时** | 字段名: null | 被过滤掉(filter `Objects::nonNull`) | + +`Result.code` 仍是 `400`,HTTP status 仍是 `200`(项目惯例),只是 `message` 字符串格式不再带 Java 字段名前缀。 + +--- + +## 一、背景 + +正式环境 admin 后台,用户编辑服务时 toast 显示 `categoryCode: 分类编码不能为空`(同日 changelog `09_frontend_notice_admin_service-edit-categorycode-required.md` 已确认前端误调 POST 创建接口为根因)。即使前端修了误调接口,**`categoryCode:` 这种字段名前缀对最终用户来说是技术噪音 — 用户不该看到 Java 字段名**,所以后端这次顺手做了体验改善。 + +`hl-common-log` 是 6 个服务的共享异常处理 module,改一处全部受益。 + +--- + +## 二、改动接口清单 + +**所有 `@Valid + @RequestBody` 接口都受影响**(数量约 200+,无法一一列举)。常见高频: + +- `POST /admin/*/save` / `POST /admin/*/create` +- `PUT /admin/*/update` +- `POST /mp/*/submit` +- 几乎所有带 `@RequestBody` 的写入接口 + +不影响: +- `@Validated` query / path 参数(走 `ConstraintViolationException`,本次未改) +- 业务异常(`BizException` 等) +- 系统异常(NPE / 序列化错误等) + +--- + +## 三、对前端 (mmg) 的影响 + +### 1. 必看:有没有 `message` 解析逻辑 + +如果 hl-ui / 小程序里有以下代码,**会失效**: + +```js +// 类似这样的 message 解析逻辑都需要排查 +if (response.message.startsWith('categoryCode:')) { ... } +const [field, msg] = response.message.split(':') +const fieldName = response.message.match(/^(\w+):/)?.[1] +``` + +请 mmg 全局搜以下关键字检查: +- `\.message\.startsWith\(` +- `\.message\.split\(':?'\)` +- `\.message\.match` +- `\.message\.indexOf\(':'\)` + +### 2. 实际更友好 + +绝大多数情况下,前端只是 `Toast(response.message)` 直接显示,本次改动让用户看到的提示**直接更友好**,**不需要任何前端改动**。 + +### 3. 多字段错误仍可读 + +多字段错误时仍以 `;空格` 分隔,例如: + +``` +姓名不能为空; 手机号格式不正确; 年龄必须为正数 +``` + +足以让用户看清所有问题。 + +--- + +## 四、技术细节 + +### 改动前 + +```java +// hl-common-log/.../GlobalExceptionHandler.java +String message = e.getBindingResult().getFieldErrors().stream() + .map(f -> f.getField() + ": " + f.getDefaultMessage()) // 拼字段名 + .reduce((a, b) -> a + "; " + b) + .orElse("参数校验失败"); +``` + +### 改动后 + +```java +String message = e.getBindingResult().getFieldErrors().stream() + .map(FieldError::getDefaultMessage) + .filter(Objects::nonNull) // null 兜底,避免 toast 出 "null" + .reduce((a, b) -> a + "; " + b) + .orElse("参数校验失败"); +``` + +`BindException` 处理同款改法。 + +--- + +## 五、为什么不顺手优化单个 message 文案 + +有人会想:`分类编码不能为空` 里"分类编码"对用户也不够友好,改成"请选择分类"更好。**本次不动**,理由: + +- 改全局 1 处,200+ 接口统一受益,B 方案逐字段改 message 工作量翻 100 倍且新加字段会复发 +- `defaultMessage` 文案优化属于 UX polish,可后续单独治理(并不紧急) +- 主目标是**去掉字段名前缀这个技术噪音**,主目标已达成 + +--- + +## 六、未覆盖 + +- `ConstraintViolationException`(`@Validated` 修饰 Controller + query/path 参数路径,如 `@RequestParam @Min(1)`)未改 — line 191 `e.getMessage()` 自带 `arg0.fieldName: 消息` 格式。本次主线 toast 不在此路径,风险/收益偏低,留待下次治理。 +- 业务异常 `BizException`、自定义异常文案不动。 + +--- + +## 验收 + +无前端改动情况下,任何 `@Valid + @RequestBody` 接口校验失败时,前端 `response.message` 应不再带 `字段名: ` 前缀。