第一个应用 #
一、创建服务器 #
1.1 最简服务器 #
创建一个最简单的Hapi服务器:
javascript
const Hapi = require('@hapi/hapi');
const init = async () => {
const server = Hapi.server({
port: 3000,
host: 'localhost'
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
init();
1.2 服务器配置选项 #
javascript
const server = Hapi.server({
port: 3000,
host: 'localhost',
app: {
customConfig: 'value'
},
routes: {
cors: true,
validate: {
failAction: (request, h, err) => {
throw err;
}
}
}
});
1.3 服务器信息 #
javascript
await server.start();
console.log('服务器URI:', server.info.uri);
console.log('服务器地址:', server.info.address);
console.log('服务器端口:', server.info.port);
console.log('服务器协议:', server.info.protocol);
二、Hello World #
2.1 基本路由 #
javascript
const Hapi = require('@hapi/hapi');
const init = async () => {
const server = Hapi.server({
port: 3000,
host: 'localhost'
});
server.route({
method: 'GET',
path: '/',
handler: (request, h) => {
return 'Hello World!';
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
init();
2.2 返回JSON #
javascript
server.route({
method: 'GET',
path: '/api',
handler: (request, h) => {
return {
message: 'Hello World',
timestamp: new Date().toISOString()
};
}
});
2.3 带状态码的响应 #
javascript
server.route({
method: 'GET',
path: '/created',
handler: (request, h) => {
return h.response({
message: 'Resource created'
}).code(201);
}
});
三、路由基础 #
3.1 定义路由 #
Hapi使用配置对象定义路由:
javascript
server.route({
method: 'GET',
path: '/hello',
handler: (request, h) => {
return 'Hello!';
}
});
3.2 多个路由 #
使用数组定义多个路由:
javascript
server.route([
{
method: 'GET',
path: '/',
handler: (request, h) => {
return 'Home Page';
}
},
{
method: 'GET',
path: '/about',
handler: (request, h) => {
return 'About Page';
}
},
{
method: 'GET',
path: '/contact',
handler: (request, h) => {
return 'Contact Page';
}
}
]);
3.3 路径参数 #
javascript
server.route({
method: 'GET',
path: '/users/{id}',
handler: (request, h) => {
return `User ID: ${request.params.id}`;
}
});
3.4 可选参数 #
javascript
server.route({
method: 'GET',
path: '/users/{id?}',
handler: (request, h) => {
if (request.params.id) {
return `User ID: ${request.params.id}`;
}
return 'All Users';
}
});
3.5 多段参数 #
javascript
server.route({
method: 'GET',
path: '/files/{path*}',
handler: (request, h) => {
return `File path: ${request.params.path}`;
}
});
访问 /files/docs/api/users 返回 File path: docs/api/users
四、处理函数 #
4.1 基本处理函数 #
javascript
const handler = (request, h) => {
return 'Response';
};
server.route({
method: 'GET',
path: '/simple',
handler: handler
});
4.2 异步处理函数 #
javascript
server.route({
method: 'GET',
path: '/async',
handler: async (request, h) => {
const data = await fetchData();
return data;
}
});
4.3 返回不同类型 #
javascript
server.route({
method: 'GET',
path: '/json',
handler: (request, h) => {
return { type: 'json' };
}
});
server.route({
method: 'GET',
path: '/text',
handler: (request, h) => {
return h.response('Plain text').type('text/plain');
}
});
server.route({
method: 'GET',
path: '/html',
handler: (request, h) => {
return h.response('<h1>HTML</h1>').type('text/html');
}
});
五、响应对象 #
5.1 h.response() #
javascript
server.route({
method: 'GET',
path: '/custom',
handler: (request, h) => {
return h.response({ message: 'Custom response' })
.code(200)
.header('X-Custom-Header', 'value')
.type('application/json');
}
});
5.2 设置响应头 #
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');
}
});
5.3 设置状态码 #
javascript
server.route({
method: 'POST',
path: '/create',
handler: (request, h) => {
return h.response({ id: 1 })
.code(201)
.message('Resource created');
}
});
5.4 重定向 #
javascript
server.route({
method: 'GET',
path: '/redirect',
handler: (request, h) => {
return h.redirect('/target');
}
});
server.route({
method: 'GET',
path: '/redirect-permanent',
handler: (request, h) => {
return h.redirect('/target').permanent(true);
}
});
六、请求对象 #
6.1 获取请求信息 #
javascript
server.route({
method: 'GET',
path: '/info',
handler: (request, h) => {
return {
method: request.method,
path: request.path,
headers: request.headers,
query: request.query,
params: request.params,
info: {
remoteAddress: request.info.remoteAddress,
host: request.info.host
}
};
}
});
6.2 获取查询参数 #
javascript
server.route({
method: 'GET',
path: '/search',
handler: (request, h) => {
const { q, page, limit } = request.query;
return {
query: q,
page: page || 1,
limit: limit || 10
};
}
});
访问 /search?q=hapi&page=2&limit=20
6.3 获取请求体 #
javascript
server.route({
method: 'POST',
path: '/users',
handler: (request, h) => {
const userData = request.payload;
return {
message: 'User created',
data: userData
};
}
});
七、错误处理 #
7.1 使用Boom #
javascript
const Boom = require('@hapi/boom');
server.route({
method: 'GET',
path: '/users/{id}',
handler: (request, h) => {
const user = findUser(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: 'GET',
path: '/errors/{type}',
handler: (request, h) => {
const { type } = request.params;
switch (type) {
case 'badRequest':
throw Boom.badRequest('Invalid request');
case 'unauthorized':
throw Boom.unauthorized('Authentication required');
case 'forbidden':
throw Boom.forbidden('Access denied');
case 'notFound':
throw Boom.notFound('Resource not found');
case 'conflict':
throw Boom.conflict('Resource already exists');
case 'internal':
throw Boom.internal('Internal server error');
default:
return { message: 'No error' };
}
}
});
7.3 全局错误处理 #
javascript
server.ext('onPreResponse', (request, h) => {
const response = request.response;
if (response.isBoom) {
const error = response;
return h.response({
error: {
statusCode: error.output.statusCode,
message: error.message,
details: error.data
}
}).code(error.output.statusCode);
}
return h.continue;
});
八、完整示例 #
8.1 用户API #
javascript
const Hapi = require('@hapi/hapi');
const Boom = require('@hapi/boom');
const users = [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' }
];
const init = async () => {
const server = Hapi.server({
port: 3000,
host: 'localhost'
});
server.route([
{
method: 'GET',
path: '/users',
handler: (request, h) => {
return users;
}
},
{
method: 'GET',
path: '/users/{id}',
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',
handler: (request, h) => {
const user = {
id: users.length + 1,
...request.payload
};
users.push(user);
return h.response(user).code(201);
}
},
{
method: 'PUT',
path: '/users/{id}',
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}',
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();
九、运行和测试 #
9.1 启动服务器 #
bash
node server.js
9.2 测试API #
bash
curl http://localhost:3000/users
curl http://localhost:3000/users/1
curl -X POST -H "Content-Type: application/json" -d '{"name":"Charlie","email":"charlie@example.com"}' http://localhost:3000/users
十、总结 #
第一个应用要点:
| 概念 | 说明 |
|---|---|
| 服务器 | Hapi.server()创建 |
| 路由 | server.route()定义 |
| 处理函数 | handler处理请求 |
| 响应 | h.response()创建响应 |
| 错误 | Boom处理错误 |
下一步,让我们学习Hapi应用的最佳结构!
最后更新:2026-03-28