插件概念 #
一、什么是插件? #
1.1 插件定义 #
插件是Hapi中扩展服务器功能的核心机制。通过插件,你可以:
- 添加新功能
- 组织代码结构
- 实现模块化开发
- 共享通用功能
1.2 插件结构 #
一个基本的插件结构:
javascript
const myPlugin = {
name: 'my-plugin',
version: '1.0.0',
register: async function (server, options) {
server.route({
method: 'GET',
path: '/plugin',
handler: (request, h) => {
return 'Hello from plugin!';
}
});
}
};
1.3 插件属性 #
| 属性 | 类型 | 说明 |
|---|---|---|
| name | string | 插件名称(必需) |
| version | string | 插件版本 |
| register | function | 注册函数(必需) |
| dependencies | array | 依赖的其他插件 |
| once | boolean | 是否只注册一次 |
| requirements | object | Hapi版本要求 |
二、插件的作用 #
2.1 添加路由 #
javascript
const userRoutesPlugin = {
name: 'user-routes',
register: async (server, options) => {
server.route([
{
method: 'GET',
path: '/users',
handler: (request, h) => {
return { users: [] };
}
},
{
method: 'POST',
path: '/users',
handler: (request, h) => {
return { message: 'Created' };
}
}
]);
}
};
2.2 添加扩展点 #
javascript
const loggerPlugin = {
name: 'logger',
register: async (server, options) => {
server.ext('onRequest', (request, h) => {
console.log(`[${new Date().toISOString()}] ${request.method} ${request.path}`);
return h.continue;
});
}
};
2.3 添加方法 #
javascript
const utilsPlugin = {
name: 'utils',
register: async (server, options) => {
server.method('formatDate', (date) => {
return new Date(date).toISOString();
});
}
};
2.4 添加装饰 #
javascript
const decoratorsPlugin = {
name: 'decorators',
register: async (server, options) => {
server.decorate('server', 'success', function (data) {
return { success: true, data };
});
server.decorate('request', 'getUser', function () {
return this.auth.credentials;
});
}
};
三、插件架构 #
3.1 服务器与插件关系 #
text
Server
├── Plugin A
│ ├── Routes
│ ├── Methods
│ └── Extensions
├── Plugin B
│ ├── Routes
│ └── Decorators
└── Plugin C
└── Auth Strategy
3.2 插件作用域 #
javascript
const pluginA = {
name: 'plugin-a',
register: async (server, options) => {
server.bind({ prefix: 'A' });
server.route({
method: 'GET',
path: '/test',
handler: function () {
return this.prefix;
}
});
}
};
3.3 插件隔离 #
每个插件有自己的作用域,互不干扰:
javascript
const pluginA = {
name: 'plugin-a',
register: async (server, options) => {
server.app.dataA = 'Data from A';
}
};
const pluginB = {
name: 'plugin-b',
register: async (server, options) => {
server.app.dataB = 'Data from B';
}
};
四、插件依赖 #
4.1 声明依赖 #
javascript
const authPlugin = {
name: 'auth',
register: async (server, options) => {
server.auth.scheme('custom', customAuthScheme);
server.auth.strategy('default', 'custom');
}
};
const userPlugin = {
name: 'users',
dependencies: ['auth'],
register: async (server, options) => {
server.route({
method: 'GET',
path: '/profile',
options: {
auth: 'default'
},
handler: (request, h) => {
return request.auth.credentials;
}
});
}
};
4.2 多个依赖 #
javascript
const apiPlugin = {
name: 'api',
dependencies: ['auth', 'database', 'cache'],
register: async (server, options) => {
}
};
五、插件类型 #
5.1 功能插件 #
提供特定功能:
javascript
const emailPlugin = {
name: 'email',
register: async (server, options) => {
const transporter = createTransporter(options);
server.method('sendEmail', async (to, subject, body) => {
return transporter.sendMail({ to, subject, html: body });
});
}
};
5.2 路由插件 #
组织路由:
javascript
const apiV1Plugin = {
name: 'api-v1',
register: async (server, options) => {
server.route([
{ method: 'GET', path: '/v1/users', handler: getUsers },
{ method: 'GET', path: '/v1/products', handler: getProducts }
]);
}
};
5.3 中间件插件 #
提供中间件功能:
javascript
const rateLimitPlugin = {
name: 'rate-limit',
register: async (server, options) => {
const limiter = createLimiter(options);
server.ext('onRequest', async (request, h) => {
const allowed = await limiter.check(request.info.remoteAddress);
if (!allowed) {
throw Boom.tooManyRequests('Rate limit exceeded');
}
return h.continue;
});
}
};
5.4 认证插件 #
提供认证策略:
javascript
const jwtPlugin = {
name: 'jwt-auth',
register: async (server, options) => {
server.auth.scheme('jwt', (server, options) => {
return {
authenticate: async (request, h) => {
const token = request.headers.authorization;
const credentials = verifyToken(token, options.secret);
return h.authenticated({ credentials });
}
};
});
server.auth.strategy('jwt', 'jwt', options);
}
};
六、插件配置 #
6.1 插件选项 #
javascript
const dbPlugin = {
name: 'database',
register: async (server, options) => {
const { host, port, name } = options;
const connection = await connect({ host, port, name });
server.decorate('server', 'db', connection);
}
};
await server.register({
plugin: dbPlugin,
options: {
host: 'localhost',
port: 5432,
name: 'myapp'
}
});
6.2 默认选项 #
javascript
const cachePlugin = {
name: 'cache',
register: async (server, options) => {
const config = {
host: options.host || 'localhost',
port: options.port || 6379,
ttl: options.ttl || 3600
};
const client = createCacheClient(config);
server.decorate('server', 'cache', client);
}
};
七、插件生命周期 #
7.1 注册阶段 #
javascript
const lifecyclePlugin = {
name: 'lifecycle',
register: async (server, options) => {
console.log('1. Plugin registering...');
server.events.on('start', () => {
console.log('3. Server started');
});
server.events.on('stop', () => {
console.log('4. Server stopped');
});
console.log('2. Plugin registered');
}
};
7.2 初始化顺序 #
text
1. 服务器创建
2. 插件注册(按依赖顺序)
3. 服务器启动
4. 处理请求
5. 服务器停止
八、插件最佳实践 #
8.1 单一职责 #
每个插件只做一件事:
javascript
const goodPlugin = {
name: 'email-sender',
register: async (server, options) => {
server.method('sendEmail', sendEmail);
}
};
const badPlugin = {
name: 'utils',
register: async (server, options) => {
server.method('sendEmail', sendEmail);
server.method('formatDate', formatDate);
server.method('validateUser', validateUser);
server.method('logError', logError);
}
};
8.2 配置驱动 #
使用配置选项:
javascript
const plugin = {
name: 'feature',
register: async (server, options) => {
if (options.enabled) {
server.route(options.routes);
}
}
};
8.3 错误处理 #
javascript
const plugin = {
name: 'database',
register: async (server, options) => {
try {
const connection = await connect(options);
server.decorate('server', 'db', connection);
} catch (error) {
console.error('Database connection failed:', error);
throw error;
}
}
};
8.4 清理资源 #
javascript
const plugin = {
name: 'redis',
register: async (server, options) => {
const client = createRedisClient(options);
server.decorate('server', 'redis', client);
server.events.on('stop', () => {
client.quit();
});
}
};
九、插件示例 #
9.1 日志插件 #
javascript
const loggingPlugin = {
name: 'logging',
version: '1.0.0',
register: async (server, options) => {
server.events.on('response', (request) => {
const log = {
timestamp: new Date().toISOString(),
method: request.method,
path: request.path,
status: request.response.statusCode,
duration: request.info.responded - request.info.received
};
console.log(JSON.stringify(log));
});
}
};
9.2 错误处理插件 #
javascript
const errorPlugin = {
name: 'error-handler',
register: async (server, options) => {
server.ext('onPreResponse', (request, h) => {
const response = request.response;
if (response.isBoom) {
const error = response;
return h.response({
error: {
status: error.output.statusCode,
message: error.message,
details: error.data
}
}).code(error.output.statusCode);
}
return h.continue;
});
}
};
9.3 CORS插件 #
javascript
const corsPlugin = {
name: 'cors',
register: async (server, options) => {
server.ext('onPreResponse', (request, h) => {
const response = request.response;
if (response.isBoom) {
response.output.headers['Access-Control-Allow-Origin'] = options.origin || '*';
} else {
response.header('Access-Control-Allow-Origin', options.origin || '*');
}
return h.continue;
});
}
};
十、总结 #
插件概念要点:
| 概念 | 说明 |
|---|---|
| name | 插件名称 |
| register | 注册函数 |
| dependencies | 插件依赖 |
| options | 配置选项 |
| 作用域 | 插件隔离 |
下一步,让我们学习如何注册和使用插件!
最后更新:2026-03-28