refactor(api): 请求体解析收成 readJson / parseBody

54 处 handler 都手写同一段:
  const parsed = X.safeParse(await c.req.json().catch(() => null))
  if (!parsed.success) return failure(c, 400, "invalid-request", ...)
漏写 .catch 就是一个畸形请求打出 500。

- http.ts 加 readJson(读 + 校验)和 parseBody(再加上 400)。parseBody 不给
  message 时回 zod 的第一条 issue —— 后台表单原来就是这么写的,fallback 文案
  「Invalid payload」/「参数错误」只在 issues 为空时才用得上,实际走不到
- 51 处换成 parseBody;另 3 处失败时带别的错误码或和路径参数一起判,换成 readJson
- 错误码、状态码、文案、判断顺序都不变

实跑核过:非 JSON 请求体回原来的固定文案,公告标题为空回 zod issue。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xuyueandClaude Opus 5.5 committed 2026-09-27 15:04:19 +08:00
1 parent c43da01ff2
commit f2ca34fd4b
21 files changed
+234 -432

No files matched your search

+11 -9
View File
@@ -43,7 +43,7 @@ import { onlineUserIds } from "../auth/presence"
import { optionalAuth, requireAuth, type AppEnv } from "../auth/middleware"
import { config } from "../config"
import { db, schema } from "../db"
import { failure, success } from "../http"
import { failure, parseBody, success } from "../http"
import { JudgeStatus } from "../judge/status"
import { getBooleanOption } from "../services/options"
import { getUserProfileById } from "../services/profile"
@@ -70,11 +70,12 @@ export const accountRoutes = new Hono<AppEnv>()
const REGISTER_PER_IP: AttemptRule = { limit: 100, windowSeconds: 60 * 60 }
accountRoutes.post("/users", async (c) => {
const parsed = registerRequestSchema.safeParse(
await c.req.json().catch(() => null),
const parsed = await parseBody(
c,
registerRequestSchema,
"Invalid registration payload",
)
if (!parsed.success)
return failure(c, 400, "invalid-request", "Invalid registration payload")
if (!parsed.success) return parsed.response
if (!(await getBooleanOption("allow_register", true))) {
return failure(
c,
@@ -173,11 +174,12 @@ accountRoutes.get("/profiles/:username", optionalAuth, async (c) => {
})
accountRoutes.put("/me/profile", requireAuth, async (c) => {
const parsed = updateProfileRequestSchema.safeParse(
await c.req.json().catch(() => null),
const parsed = await parseBody(
c,
updateProfileRequestSchema,
"Invalid profile payload",
)
if (!parsed.success)
return failure(c, 400, "invalid-request", "Invalid profile payload")
if (!parsed.success) return parsed.response
const values = Object.fromEntries(
Object.entries(parsed.data).map(([key, value]) => [
key,