生命周期 #

一、生命周期概述 #

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