路由参数 #
一、路径参数 #
1.1 基本路径参数 #
使用 {param} 语法定义路径参数:
javascript
server.route({
method: 'GET',
path: '/users/{id}',
handler: (request, h) => {
return { userId: request.params.id };
}
});
访问 /users/123 返回:
json
{
"userId": "123"
}
1.2 多个路径参数 #
javascript
server.route({
method: 'GET',
path: '/users/{userId}/posts/{postId}',
handler: (request, h) => {
const { userId, postId } = request.params;
return {
userId,
postId
};
}
});
访问 /users/1/posts/42
1.3 可选参数 #
使用 {param?} 定义可选参数:
javascript
server.route({
method: 'GET',
path: '/search/{keyword?}',
handler: (request, h) => {
if (request.params.keyword) {
return { keyword: request.params.keyword };
}
return { message: '请输入搜索关键词' };
}
});
1.4 多段参数 #
使用 {param*} 匹配多段路径:
javascript
server.route({
method: 'GET',
path: '/files/{path*}',
handler: (request, h) => {
return {
path: request.params.path,
segments: request.params.path.split('/')
};
}
});
访问 /files/docs/api/users:
json
{
"path": "docs/api/users",
"segments": ["docs", "api", "users"]
}
1.5 限制段数 #
javascript
server.route({
method: 'GET',
path: '/category/{path*2}',
handler: (request, h) => {
return { path: request.params.path };
}
});
只能匹配两段,如 /category/electronics/phones
二、参数验证 #
2.1 基本验证 #
javascript
const Joi = require('joi');
server.route({
method: 'GET',
path: '/users/{id}',
options: {
validate: {
params: Joi.object({
id: Joi.number().integer().positive().required()
})
}
},
handler: (request, h) => {
return { id: request.params.id };
}
});
2.2 字符串参数验证 #
javascript
server.route({
method: 'GET',
path: '/products/{sku}',
options: {
validate: {
params: Joi.object({
sku: Joi.string().alphanum().length(8).required()
})
}
},
handler: (request, h) => {
return { sku: request.params.sku };
}
});
2.3 正则验证 #
javascript
server.route({
method: 'GET',
path: '/orders/{orderId}',
options: {
validate: {
params: Joi.object({
orderId: Joi.string().regex(/^[A-Z]{2}\d{6}$/)
})
}
},
handler: (request, h) => {
return { orderId: request.params.orderId };
}
});
2.4 多参数验证 #
javascript
server.route({
method: 'GET',
path: '/users/{userId}/posts/{postId}',
options: {
validate: {
params: Joi.object({
userId: Joi.number().integer().positive().required(),
postId: Joi.number().integer().positive().required()
})
}
},
handler: (request, h) => {
return request.params;
}
});
2.5 自定义验证 #
javascript
server.route({
method: 'GET',
path: '/items/{code}',
options: {
validate: {
params: Joi.object({
code: Joi.string().custom((value, helpers) => {
if (!value.startsWith('ITEM-')) {
return helpers.error('any.invalid');
}
return value;
})
})
}
},
handler: (request, h) => {
return { code: request.params.code };
}
});
三、查询参数 #
3.1 获取查询参数 #
javascript
server.route({
method: 'GET',
path: '/search',
handler: (request, h) => {
return {
query: request.query
};
}
});
访问 /search?q=hapi&page=1&limit=10
3.2 查询参数验证 #
javascript
server.route({
method: 'GET',
path: '/users',
options: {
validate: {
query: Joi.object({
name: Joi.string(),
email: Joi.string().email(),
page: Joi.number().integer().min(1).default(1),
limit: Joi.number().integer().min(1).max(100).default(10),
sort: Joi.string().valid('name', 'email', 'createdAt').default('createdAt'),
order: Joi.string().valid('asc', 'desc').default('desc')
})
}
},
handler: (request, h) => {
return request.query;
}
});
3.3 可选查询参数 #
javascript
server.route({
method: 'GET',
path: '/products',
options: {
validate: {
query: Joi.object({
category: Joi.string(),
minPrice: Joi.number().min(0),
maxPrice: Joi.number().min(Joi.ref('minPrice')),
inStock: Joi.boolean()
})
}
},
handler: (request, h) => {
const filters = request.query;
return { filters };
}
});
3.4 数组查询参数 #
javascript
server.route({
method: 'GET',
path: '/articles',
options: {
validate: {
query: Joi.object({
tags: Joi.array().items(Joi.string()).single(),
ids: Joi.array().items(Joi.number()).single()
})
}
},
handler: (request, h) => {
return request.query;
}
});
访问 /articles?tags=node&tags=hapi&ids=1&ids=2
3.5 对象查询参数 #
javascript
server.route({
method: 'GET',
path: '/search',
options: {
validate: {
query: Joi.object({
filter: Joi.object({
name: Joi.string(),
age: Joi.number()
})
})
}
},
handler: (request, h) => {
return request.query.filter;
}
});
访问 /search?filter[name]=John&filter[age]=30
四、请求体参数 #
4.1 JSON请求体 #
javascript
server.route({
method: 'POST',
path: '/users',
options: {
validate: {
payload: Joi.object({
name: Joi.string().min(2).max(50).required(),
email: Joi.string().email().required(),
password: Joi.string().min(6).required(),
age: Joi.number().integer().min(0).max(120)
})
}
},
handler: (request, h) => {
return request.payload;
}
});
4.2 嵌套对象验证 #
javascript
server.route({
method: 'POST',
path: '/orders',
options: {
validate: {
payload: Joi.object({
customer: Joi.object({
name: Joi.string().required(),
email: Joi.string().email().required(),
address: Joi.object({
street: Joi.string().required(),
city: Joi.string().required(),
zipCode: Joi.string().required()
}).required()
}).required(),
items: Joi.array().items(
Joi.object({
productId: Joi.number().required(),
quantity: Joi.number().min(1).required()
})
).min(1).required()
})
}
},
handler: (request, h) => {
return request.payload;
}
});
4.3 条件验证 #
javascript
server.route({
method: 'POST',
path: '/users',
options: {
validate: {
payload: Joi.object({
type: Joi.string().valid('individual', 'company').required(),
name: Joi.string().when('type', {
is: 'individual',
then: Joi.required()
}),
companyName: Joi.string().when('type', {
is: 'company',
then: Joi.required()
})
})
}
},
handler: (request, h) => {
return request.payload;
}
});
五、参数转换 #
5.1 类型转换 #
javascript
server.route({
method: 'GET',
path: '/items/{id}',
options: {
validate: {
params: Joi.object({
id: Joi.number().integer()
})
}
},
handler: (request, h) => {
return {
id: request.params.id,
type: typeof request.params.id
};
}
});
5.2 默认值 #
javascript
server.route({
method: 'GET',
path: '/list',
options: {
validate: {
query: Joi.object({
page: Joi.number().default(1),
limit: Joi.number().default(10),
sort: Joi.string().default('name')
})
}
},
handler: (request, h) => {
return request.query;
}
});
5.3 值转换 #
javascript
server.route({
method: 'GET',
path: '/search',
options: {
validate: {
query: Joi.object({
q: Joi.string().trim().lowercase(),
active: Joi.boolean().truthy('yes', '1', true).falsy('no', '0', false)
})
}
},
handler: (request, h) => {
return request.query;
}
});
六、参数验证失败处理 #
6.1 默认处理 #
javascript
server.route({
method: 'GET',
path: '/users/{id}',
options: {
validate: {
params: Joi.object({
id: Joi.number().required()
}),
failAction: (request, h, err) => {
throw err;
}
}
},
handler: (request, h) => {
return { id: request.params.id };
}
});
6.2 自定义错误消息 #
javascript
server.route({
method: 'POST',
path: '/users',
options: {
validate: {
payload: Joi.object({
name: Joi.string().min(2).required()
.messages({
'string.empty': '姓名不能为空',
'string.min': '姓名至少需要{#limit}个字符',
'any.required': '姓名是必填字段'
}),
email: Joi.string().email().required()
.messages({
'string.email': '请输入有效的邮箱地址'
})
}),
failAction: (request, h, err) => {
const errors = err.details.map(d => ({
field: d.path.join('.'),
message: d.message
}));
return h.response({ errors }).code(400).takeover();
}
}
},
handler: (request, h) => {
return request.payload;
}
});
6.3 全局验证失败处理 #
javascript
const server = Hapi.server({
port: 3000,
routes: {
validate: {
failAction: async (request, h, err) => {
if (process.env.NODE_ENV === 'production') {
console.error(err);
return h.response({ error: 'Invalid request' }).code(400).takeover();
}
throw err;
}
}
}
});
七、参数组合验证 #
7.1 跨参数验证 #
javascript
server.route({
method: 'POST',
path: '/events',
options: {
validate: {
payload: Joi.object({
startDate: Joi.date().required(),
endDate: Joi.date().greater(Joi.ref('startDate')).required()
})
}
},
handler: (request, h) => {
return request.payload;
}
});
7.2 参数依赖 #
javascript
server.route({
method: 'POST',
path: '/shipping',
options: {
validate: {
payload: Joi.object({
method: Joi.string().valid('pickup', 'delivery').required(),
address: Joi.string().when('method', {
is: 'delivery',
then: Joi.required()
})
})
}
},
handler: (request, h) => {
return request.payload;
}
});
八、完整示例 #
8.1 用户API #
javascript
const Joi = require('joi');
const Boom = require('@hapi/boom');
const userSchema = Joi.object({
name: Joi.string().min(2).max(50).required(),
email: Joi.string().email().required(),
age: Joi.number().integer().min(0).max(120)
});
server.route([
{
method: 'GET',
path: '/users',
options: {
validate: {
query: Joi.object({
page: Joi.number().default(1),
limit: Joi.number().default(10)
})
}
},
handler: async (request, h) => {
const { page, limit } = request.query;
return { users: [], page, limit };
}
},
{
method: 'GET',
path: '/users/{id}',
options: {
validate: {
params: Joi.object({
id: Joi.number().integer().positive().required()
})
}
},
handler: async (request, h) => {
const user = await findUser(request.params.id);
if (!user) {
throw Boom.notFound('User not found');
}
return user;
}
},
{
method: 'POST',
path: '/users',
options: {
validate: {
payload: userSchema
}
},
handler: async (request, h) => {
const user = await createUser(request.payload);
return h.response(user).code(201);
}
}
]);
九、总结 #
参数处理要点:
| 参数类型 | 获取方式 | 验证配置 |
|---|---|---|
| 路径参数 | request.params | validate.params |
| 查询参数 | request.query | validate.query |
| 请求体 | request.payload | validate.payload |
| 请求头 | request.headers | validate.headers |
下一步,让我们深入学习路由处理!
最后更新:2026-03-28