生命周期 #
一、生命周期概述 #
1.1 什么是生命周期? #
Hapi请求生命周期是指一个HTTP请求从进入服务器到返回响应所经历的所有阶段。
1.2 生命周期阶段 #
text
请求进入
↓
onRequest
↓
onPreAuth
↓
认证
↓
onPostAuth
↓
onPreHandler
↓
Handler
↓
onPostHandler
↓
onPreResponse
↓
响应发送
二、扩展点 #
2.1 onRequest #
请求进入时的第一个扩展点:
javascript
server.ext('onRequest', (request, h) => {
console.log('Request received:', request.path);
return h.continue;
});
2.2 onPreAuth #
认证前的扩展点:
javascript
server.ext('onPreAuth', (request, h) => {
console.log('Before authentication');
return h.continue;
});
2.3 onPostAuth #
认证后的扩展点:
javascript
server.ext('onPostAuth', (request, h) => {
if (request.auth.isAuthenticated) {
console.log('User:', request.auth.credentials.id);
}
return h.continue;
});
2.4 onPreHandler #
处理函数前的扩展点:
javascript
server.ext('onPreHandler', (request, h) => {
request.app.startTime = Date.now();
return h.continue;
});
2.5 onPostHandler #
处理函数后的扩展点:
javascript
server.ext('onPostHandler', (request, h) => {
console.log('Handler completed');
return h.continue;
});
2.6 onPreResponse #
响应前的最后一个扩展点:
javascript
server.ext('onPreResponse', (request, h) => {
const response = request.response;
if (response.isBoom) {
return h.response({
error: response.message
}).code(response.output.statusCode);
}
return h.continue;
});
三、扩展点详解 #
3.1 onRequest #
用于请求预处理:
javascript
server.ext('onRequest', (request, h) => {
request.app.requestId = generateRequestId();
request.app.startTime = Date.now();
console.log(`[${request.app.requestId}] ${request.method.toUpperCase()} ${request.path}`);
return h.continue;
});
3.2 onPreAuth #
用于认证前检查:
javascript
server.ext('onPreAuth', (request, h) => {
if (request.path.startsWith('/api/')) {
request.app.isApiRequest = true;
}
return h.continue;
});
3.3 onPostAuth #
用于认证后处理:
javascript
server.ext('onPostAuth', (request, h) => {
if (request.auth.isAuthenticated) {
request.app.userId = request.auth.credentials.id;
}
return h.continue;
});
3.4 onPreHandler #
用于处理函数前的准备:
javascript
server.ext('onPreHandler', (request, h) => {
if (request.method === 'post' || request.method === 'put') {
request.payload = sanitize(request.payload);
}
return h.continue;
});
3.5 onPostHandler #
用于处理函数后的修改:
javascript
server.ext('onPostHandler', (request, h) => {
const response = request.response;
if (!response.isBoom) {
response.source.timestamp = new Date().toISOString();
}
return h.continue;
});
3.6 onPreResponse #
用于统一响应格式:
javascript
server.ext('onPreResponse', (request, h) => {
const response = request.response;
if (response.isBoom) {
const error = response;
return h.response({
success: false,
error: {
code: error.output.statusCode,
message: error.message,
details: error.data
}
}).code(error.output.statusCode);
}
return h.response({
success: true,
data: response.source,
timestamp: new Date().toISOString()
});
});
四、扩展点选项 #
4.1 优先级 #
javascript
server.ext('onRequest', highPriorityHandler, { before: 'other-plugin' });
server.ext('onRequest', lowPriorityHandler, { after: 'other-plugin' });
4.2 绑定上下文 #
javascript
server.ext('onRequest', function (request, h) {
console.log(this.message);
return h.continue;
}, { bind: { message: 'Hello' } });
4.3 指定插件 #
javascript
server.ext({
type: 'onRequest',
method: (request, h) => {
return h.continue;
},
options: {
plugin: 'my-plugin'
}
});
五、常见应用场景 #
5.1 请求日志 #
javascript
server.ext('onRequest', (request, h) => {
request.app.startTime = Date.now();
return h.continue;
});
server.ext('onPreResponse', (request, h) => {
const duration = Date.now() - request.app.startTime;
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
method: request.method,
path: request.path,
status: request.response.statusCode,
duration: duration
}));
return h.continue;
});
5.2 错误处理 #
javascript
server.ext('onPreResponse', (request, h) => {
const response = request.response;
if (response.isBoom) {
const error = response;
console.error({
timestamp: new Date().toISOString(),
error: error.message,
stack: error.stack
});
return h.response({
success: false,
error: {
code: error.output.statusCode,
message: error.message
}
}).code(error.output.statusCode);
}
return h.continue;
});
5.3 响应包装 #
javascript
server.ext('onPreResponse', (request, h) => {
const response = request.response;
if (!response.isBoom && !request.path.startsWith('/docs')) {
const original = response.source;
response.source = {
success: true,
data: original,
meta: {
timestamp: new Date().toISOString(),
requestId: request.app.requestId
}
};
}
return h.continue;
});
5.4 CORS处理 #
javascript
server.ext('onPreResponse', (request, h) => {
const response = request.response;
const headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization'
};
Object.keys(headers).forEach(key => {
if (response.isBoom) {
response.output.headers[key] = headers[key];
} else {
response.header(key, headers[key]);
}
});
return h.continue;
});
5.5 请求验证 #
javascript
server.ext('onPostAuth', (request, h) => {
if (request.route.settings.auth !== false) {
if (!request.auth.isAuthenticated) {
throw Boom.unauthorized('Please login');
}
const user = request.auth.credentials;
if (!user.active) {
throw Boom.forbidden('Account is inactive');
}
}
return h.continue;
});
六、服务器事件 #
6.1 服务器启动 #
javascript
server.events.on('start', () => {
console.log('Server started at:', new Date().toISOString());
});
6.2 服务器停止 #
javascript
server.events.on('stop', () => {
console.log('Server stopped at:', new Date().toISOString());
});
6.3 请求完成 #
javascript
server.events.on('response', (request) => {
console.log({
method: request.method,
path: request.path,
status: request.response.statusCode,
duration: request.info.responded - request.info.received
});
});
6.4 请求日志 #
javascript
server.events.on({ name: 'request', channels: 'error' }, (request, event) => {
console.error('Request error:', event.error);
});
七、完整示例 #
7.1 完整生命周期示例 #
javascript
const Hapi = require('@hapi/hapi');
const init = async () => {
const server = Hapi.server({ port: 3000 });
server.ext('onRequest', (request, h) => {
request.app.requestId = Date.now().toString(36);
request.app.startTime = Date.now();
console.log(`[${request.app.requestId}] Request started: ${request.method} ${request.path}`);
return h.continue;
});
server.ext('onPreAuth', (request, h) => {
console.log(`[${request.app.requestId}] Before auth`);
return h.continue;
});
server.ext('onPostAuth', (request, h) => {
console.log(`[${request.app.requestId}] After auth`);
return h.continue;
});
server.ext('onPreHandler', (request, h) => {
console.log(`[${request.app.requestId}] Before handler`);
return h.continue;
});
server.ext('onPostHandler', (request, h) => {
console.log(`[${request.app.requestId}] After handler`);
return h.continue;
});
server.ext('onPreResponse', (request, h) => {
const duration = Date.now() - request.app.startTime;
console.log(`[${request.app.requestId}] Response sent (${duration}ms)`);
const response = request.response;
if (response.isBoom) {
return h.response({
success: false,
error: response.message
}).code(response.output.statusCode);
}
return h.continue;
});
server.route({
method: 'GET',
path: '/',
handler: (request, h) => {
return { message: 'Hello World' };
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
init();
八、总结 #
生命周期要点:
| 扩展点 | 时机 |
|---|---|
| onRequest | 请求进入时 |
| onPreAuth | 认证前 |
| onPostAuth | 认证后 |
| onPreHandler | 处理函数前 |
| onPostHandler | 处理函数后 |
| onPreResponse | 响应前 |
下一步,让我们学习服务器方法!
最后更新:2026-03-28