| 版本 | 日期 | 描述 | 作者 |
|---|---|---|---|
| 1.0 | 2026-01-03 | 初始版本,基于故事010.003修复经验总结 | James (Claude Code) |
本文档定义了使用 hono/testing 的 testClient 测试API路由时的正确调用方式,避免常见的调用错误。
testClient 会根据Hono路由的路径结构自动生成类型安全的客户端对象。关键是理解路径到属性的映射规则。
/ 转换为嵌套对象:param 转换为 [':param'] 索引/ 被移除/ 被压缩为单层嵌套// 路由定义
const route = createRoute({
method: 'get',
path: '/', // ✅ 根路径
// ...
});
app.openapi(route, handler);
// 测试调用
const client = testClient(app);
const response = await client.$get({ // ✅ 使用 .$get()
query: { page: 1 }
});
// 路由定义
const route = createRoute({
method: 'post',
path: '/users', // 单层路径
// ...
});
app.openapi(route, handler);
// 测试调用
const response = await client.users.$post({ // ✅ client.users.$post()
json: userData
});
// 路由定义
const route = createRoute({
method: 'get',
path: '/:id', // 参数路径
// ...
});
app.openapi(route, handler);
// 测试调用
const response = await client[':id'].$get({ // ✅ client[':id'].$get()
param: { id: 123 }
});
// 路由定义
const route = createRoute({
method: 'put',
path: '/users/:id', // 嵌套路由+参数
// ...
});
app.openapi(route, handler);
// 测试调用
const response = await client.users[':id'].$put({ // ✅ client.users[':id'].$put()
param: { id: 123 },
json: updateData
});
// 路由定义
const route = createRoute({
method: 'get',
path: '/admin/users/:id', // 多层嵌套+参数
// ...
});
app.openapi(route, handler);
// 测试调用
const response = await client.admin.users[':id'].$get({ // ✅ 嵌套调用
param: { id: 123 }
});
// ❌ 错误:使用完整路径作为属性
const response = await client['/api/v1/users'].$get();
// ✅ 正确:去掉前缀,使用相对路径映射
const response = await client.users.$get();
// 模块路由定义为 /:id
const route = createRoute({
path: '/:id',
// ...
});
// ❌ 错误:使用了完整路径
const response = await client['/api/v1/:id'].$get();
// ✅ 正确:使用模块内的相对路径
const response = await client[':id'].$get();
:id 索引// 路由定义: path: '/users/:id'
// ❌ 错误:直接访问id属性
const response = await client.users[id].$get(); // 编译错误
// ✅ 正确:使用字符串索引
const response = await client.users[':id'].$get({
param: { id }
});
// 路由定义: path: '/admin/users/:id'
// ❌ 错误:用斜杠分隔
const response = await client['admin/users/:id'].$get();
// ❌ 错误:用点号分隔参数
const response = await client.admin.users.:id.$get();
// ✅ 正确:用嵌套对象+字符串索引
const response = await client.admin.users[':id'].$get();
模块内部路由应使用相对路径(不以 / 开头的完整路径):
// ✅ 正确:模块内使用相对路径
const listRoute = createRoute({
method: 'get',
path: '/', // 或 '/:id'
middleware: [authMiddleware],
// ...
});
// ❌ 错误:模块内使用完整API路径
const listRoute = createRoute({
method: 'get',
path: '/api/v1/admin/users', // 不应在模块内包含前缀
middleware: [authMiddleware],
// ...
});
Server包注册时添加完整前缀:
// packages/server/src/index.ts
import { unifiedAdvertisementAdminRoutes } from '@d8d/unified-advertisements-module';
// ✅ 正确:Server包注册时添加完整前缀
app.route('/api/v1/admin/unified-advertisements', unifiedAdvertisementAdminRoutes);
// ✅ 正确:测试时使用模块的相对路径
const adminClient = testClient(unifiedAdvertisementAdminRoutes);
// 路由定义: path: '/'
const response = await adminClient.$get({
query: { page: 1 }
});
// 路由定义: path: '/:id'
const response = await adminClient[':id'].$put({
param: { id: 123 },
json: data
});
| 路由定义 (模块内) | Server注册 | 测试调用方式 |
|---|---|---|
path: '/' |
.route('/api/v1/users', routes) |
client.$get() |
path: '/users' |
.route('/api/v1/users', routes) |
client.users.$get() |
path: '/:id' |
.route('/api/v1/users', routes) |
client[':id'].$get() |
path: '/users/:id' |
.route('/api/v1/users', routes) |
client.users[':id'].$get() |
path: '/admin/users/:id' |
.route('/api/v1/admin', routes) |
client.admin.users[':id'].$get() |
const client = testClient<typeof userRoutes>(userRoutes);
// IDE会自动补全可用的方法和路径
client.$get() // 如果路由有GET /
client.users.$get() // 如果路由有GET /users
client[':id'].$get() // 如果路由有GET /:id
// 查看路由文件中的 path 定义
// src/routes/users.ts
const listRoute = createRoute({
method: 'get',
path: '/', // ← 这里决定了测试调用方式
// ...
});
// 根据path确定测试调用
await client.$get() // path: '/' → .$get()
// 如果看到类似错误:
// "Cannot read property '$get' of undefined"
// 说明路径映射不正确
// 检查:
// 1. 路由 path 是否正确定义
// 2. testClient 是否使用了正确的路由实例
// 3. 是否有多余的路径前缀
| 路由 path | testClient 调用 |
|---|---|
/ |
.$get(), .$post(), etc. |
/:id |
[':id'].$get(), [':id'].$put(), etc. |
/users |
.users.$get(), .users.$post(), etc. |
/users/:id |
.users[':id'].$get(), .users[':id'].$put(), etc. |
/admin/users/:id |
.admin.users[':id'].$get(), etc. |
path: '/' 或 path: '/:id'.route('/api/v1/xxx', routes)/ → .$method(), /:id → [':id'].$method()/admin/users/:id → .admin.users[':id'].$method()文档状态: 正式版 创建原因: 故事010.003修复过程中发现缺乏testClient调用规范 适用范围: 所有使用 hono/testing 的后端模块测试