路由处理 #
一、处理函数基础 #
1.1 基本处理函数 #
javascript
server.route({
method: 'GET',
path: '/hello',
handler: (request, h) => {
return 'Hello World!';
}
});
1.2 处理函数参数 #
| 参数 | 说明 |
|---|---|
| request | 请求对象 |
| h | 响应工具包 |
1.3 返回值 #
处理函数可以返回:
- 字符串
- 对象(自动转为JSON)
- Buffer
- Stream
- Promise
- h.response() 创建的响应对象
二、请求对象 #
2.1 常用属性 #
javascript
server.route({
method: 'GET',
path: '/info',
handler: (request, h) => {
return {
method: request.method,
path: request.path,
query: request.query,
params: request.params,
headers: request.headers,
payload: request.payload,
info: {
remoteAddress: request.info.remoteAddress,
host: request.info.host,
received: request.info.received
}
};
}
});
2.2 获取认证信息 #
javascript
server.route({
method: 'GET',
path: '/profile',
options: {
auth: 'jwt'
},
handler: (request, h) => {
return {
userId: request.auth.credentials.id,
username: request.auth.credentials.username
};
}
});
2.3 获取应用状态 #
javascript
server.route({
method: 'GET',
path: '/app-info',
handler: (request, h) => {
return {
app: request.server.app,
plugins: request.server.plugins
};
}
});
三、响应工具包 #
3.1 h.response() #
创建响应对象:
javascript
server.route({
method: 'GET',
path: '/custom',
handler: (request, h) => {
return h.response({ message: 'Success' })
.code(200)
.header('X-Custom', 'value')
.type('application/json');
}
});
3.2 设置状态码 #
javascript
server.route({
method: 'POST',
path: '/create',
handler: (request, h) => {
return h.response({ id: 1 })
.code(201)
.message('Created');
}
});
3.3 设置响应头 #
javascript
server.route({
method: 'GET',
path: '/headers',
handler: (request, h) => {
return h.response('Response with headers')
.header('X-API-Version', '1.0')
.header('Cache-Control', 'no-cache')
.header('Content-Type', 'text/plain');
}
});
3.4 设置Cookie #
javascript
server.route({
method: 'POST',
path: '/login',
handler: (request, h) => {
return h.response({ message: 'Logged in' })
.state('sessionId', 'abc123', {
ttl: 24 * 60 * 60 * 1000,
isSecure: true,
isHttpOnly: true
});
}
});
3.5 清除Cookie #
javascript
server.route({
method: 'POST',
path: '/logout',
handler: (request, h) => {
return h.response({ message: 'Logged out' })
.unstate('sessionId');
}
});
四、重定向 #
4.1 基本重定向 #
javascript
server.route({
method: 'GET',
path: '/redirect',
handler: (request, h) => {
return h.redirect('/target');
}
});
4.2 永久重定向 #
javascript
server.route({
method: 'GET',
path: '/old-url',
handler: (request, h) => {
return h.redirect('/new-url').permanent(true);
}
});
4.3 临时重定向 #
javascript
server.route({
method: 'GET',
path: '/temp-redirect',
handler: (request, h) => {
return h.redirect('/target').temporary(true);
}
});
4.4 重写URL #
javascript
server.route({
method: 'GET',
path: '/rewrite',
handler: (request, h) => {
return h.redirect('/target').rewritable(false);
}
});
五、响应类型 #
5.1 JSON响应 #
javascript
server.route({
method: 'GET',
path: '/json',
handler: (request, h) => {
return {
message: 'Hello',
data: { id: 1 }
};
}
});
5.2 文本响应 #
javascript
server.route({
method: 'GET',
path: '/text',
handler: (request, h) => {
return h.response('Plain text response')
.type('text/plain')
.charset('utf-8');
}
});
5.3 HTML响应 #
javascript
server.route({
method: 'GET',
path: '/html',
handler: (request, h) => {
return h.response('<h1>Hello World</h1>')
.type('text/html');
}
});
5.4 文件响应 #
javascript
server.route({
method: 'GET',
path: '/download',
handler: (request, h) => {
return h.file('/path/to/file.pdf', {
mode: 'attachment',
filename: 'document.pdf'
});
}
});
5.5 流响应 #
javascript
const fs = require('fs');
server.route({
method: 'GET',
path: '/stream',
handler: (request, h) => {
const stream = fs.createReadStream('/path/to/file.txt');
return h.response(stream)
.type('text/plain')
.header('Content-Disposition', 'attachment');
}
});
六、异步处理 #
6.1 async/await #
javascript
server.route({
method: 'GET',
path: '/users/{id}',
handler: async (request, h) => {
const user = await User.findById(request.params.id);
return user;
}
});
6.2 Promise #
javascript
server.route({
method: 'GET',
path: '/data',
handler: (request, h) => {
return fetchData()
.then(data => {
return { data };
})
.catch(error => {
throw Boom.badImplementation(error.message);
});
}
});
6.3 并行请求 #
javascript
server.route({
method: 'GET',
path: '/dashboard',
handler: async (request, h) => {
const [users, orders, stats] = await Promise.all([
User.findAll(),
Order.findAll(),
Stats.getDaily()
]);
return { users, orders, stats };
}
});
七、错误处理 #
7.1 使用Boom #
javascript
const Boom = require('@hapi/boom');
server.route({
method: 'GET',
path: '/users/{id}',
handler: async (request, h) => {
const user = await User.findById(request.params.id);
if (!user) {
throw Boom.notFound('User not found');
}
return user;
}
});
7.2 常用Boom错误 #
javascript
const Boom = require('@hapi/boom');
server.route({
method: 'POST',
path: '/action',
handler: (request, h) => {
const { action } = request.payload;
switch (action) {
case 'badRequest':
throw Boom.badRequest('Invalid request');
case 'unauthorized':
throw Boom.unauthorized('Please login');
case 'forbidden':
throw Boom.forbidden('Access denied');
case 'notFound':
throw Boom.notFound('Resource not found');
case 'conflict':
throw Boom.conflict('Resource already exists');
case 'badImplementation':
throw Boom.badImplementation('Server error');
default:
return { message: 'OK' };
}
}
});
7.3 自定义错误数据 #
javascript
server.route({
method: 'GET',
path: '/validate',
handler: (request, h) => {
throw Boom.badRequest('Validation failed', {
errors: [
{ field: 'email', message: 'Invalid email' },
{ field: 'password', message: 'Too short' }
]
});
}
});
7.4 全局错误处理 #
javascript
server.ext('onPreResponse', (request, h) => {
const response = request.response;
if (response.isBoom) {
const error = response;
const payload = {
error: {
statusCode: error.output.statusCode,
error: error.output.payload.error,
message: error.message
}
};
if (error.data) {
payload.error.details = error.data;
}
return h.response(payload).code(error.output.statusCode);
}
return h.continue;
});
八、响应验证 #
8.1 响应Schema验证 #
javascript
server.route({
method: 'GET',
path: '/users/{id}',
options: {
response: {
schema: Joi.object({
id: Joi.number().required(),
name: Joi.string().required(),
email: Joi.string().email().required()
})
}
},
handler: async (request, h) => {
return await User.findById(request.params.id);
}
});
8.2 响应修改 #
javascript
server.route({
method: 'GET',
path: '/users',
options: {
response: {
modify: true,
options: {
stripUnknown: true
}
}
},
handler: async (request, h) => {
return await User.findAll();
}
});
8.3 响应失败处理 #
javascript
server.route({
method: 'GET',
path: '/data',
options: {
response: {
schema: Joi.object({
id: Joi.number(),
name: Joi.string()
}),
failAction: (request, h, err) => {
console.error('Response validation failed:', err);
throw err;
}
}
},
handler: (request, h) => {
return { id: 1, name: 'Test', extra: 'field' };
}
});
九、处理函数组织 #
9.1 分离处理函数 #
handlers/users.js:
javascript
const userService = require('../services/userService');
const Boom = require('@hapi/boom');
const getAll = async (request, h) => {
const { page, limit } = request.query;
return await userService.findAll({ page, limit });
};
const getById = async (request, h) => {
const user = await userService.findById(request.params.id);
if (!user) {
throw Boom.notFound('User not found');
}
return user;
};
const create = async (request, h) => {
const user = await userService.create(request.payload);
return h.response(user).code(201);
};
const update = async (request, h) => {
const user = await userService.update(request.params.id, request.payload);
return user;
};
const remove = async (request, h) => {
await userService.remove(request.params.id);
return h.response().code(204);
};
module.exports = {
getAll,
getById,
create,
update,
remove
};
9.2 使用处理函数 #
routes/users.js:
javascript
const handlers = require('../handlers/users');
module.exports = [
{
method: 'GET',
path: '/users',
handler: handlers.getAll
},
{
method: 'GET',
path: '/users/{id}',
handler: handlers.getById
},
{
method: 'POST',
path: '/users',
handler: handlers.create
},
{
method: 'PUT',
path: '/users/{id}',
handler: handlers.update
},
{
method: 'DELETE',
path: '/users/{id}',
handler: handlers.remove
}
];
十、完整示例 #
10.1 RESTful API处理 #
javascript
const Hapi = require('@hapi/hapi');
const Joi = require('joi');
const Boom = require('@hapi/boom');
const users = [];
const init = async () => {
const server = Hapi.server({ port: 3000 });
server.route([
{
method: 'GET',
path: '/users',
options: {
validate: {
query: Joi.object({
page: Joi.number().default(1),
limit: Joi.number().default(10)
})
}
},
handler: (request, h) => {
const { page, limit } = request.query;
const start = (page - 1) * limit;
const end = start + limit;
return {
users: users.slice(start, end),
total: users.length,
page,
limit
};
}
},
{
method: 'GET',
path: '/users/{id}',
options: {
validate: {
params: Joi.object({
id: Joi.number().required()
})
}
},
handler: (request, h) => {
const user = users.find(u => u.id === parseInt(request.params.id));
if (!user) {
throw Boom.notFound('User not found');
}
return user;
}
},
{
method: 'POST',
path: '/users',
options: {
validate: {
payload: Joi.object({
name: Joi.string().required(),
email: Joi.string().email().required()
})
}
},
handler: (request, h) => {
const user = {
id: users.length + 1,
...request.payload,
createdAt: new Date()
};
users.push(user);
return h.response(user).code(201);
}
},
{
method: 'PUT',
path: '/users/{id}',
options: {
validate: {
params: Joi.object({
id: Joi.number().required()
}),
payload: Joi.object({
name: Joi.string(),
email: Joi.string().email()
}).min(1)
}
},
handler: (request, h) => {
const index = users.findIndex(u => u.id === parseInt(request.params.id));
if (index === -1) {
throw Boom.notFound('User not found');
}
users[index] = { ...users[index], ...request.payload };
return users[index];
}
},
{
method: 'DELETE',
path: '/users/{id}',
options: {
validate: {
params: Joi.object({
id: Joi.number().required()
})
}
},
handler: (request, h) => {
const index = users.findIndex(u => u.id === parseInt(request.params.id));
if (index === -1) {
throw Boom.notFound('User not found');
}
users.splice(index, 1);
return h.response().code(204);
}
}
]);
await server.start();
console.log('Server running on %s', server.info.uri);
};
init();
十一、总结 #
路由处理要点:
| 概念 | 说明 |
|---|---|
| handler | 处理函数 |
| request | 请求对象 |
| h | 响应工具包 |
| h.response() | 创建响应 |
| Boom | 错误处理 |
| 验证 | 输入输出验证 |
下一步,让我们深入学习Hapi的插件系统!
最后更新:2026-03-28