编写插件 #
一、插件结构 #
1.1 基本结构 #
javascript
const myPlugin = {
name: 'my-plugin',
version: '1.0.0',
register: async function (server, options) {
}
};
module.exports = myPlugin;
1.2 完整结构 #
javascript
const myPlugin = {
name: 'my-plugin',
version: '1.0.0',
once: true,
dependencies: ['other-plugin'],
requirements: {
hapi: '>=20.0.0',
node: '>=14.0.0'
},
register: async function (server, options) {
}
};
module.exports = myPlugin;
二、添加路由 #
2.1 单个路由 #
javascript
const routesPlugin = {
name: 'custom-routes',
register: async (server, options) => {
server.route({
method: 'GET',
path: '/hello',
handler: (request, h) => {
return 'Hello from plugin!';
}
});
}
};
2.2 多个路由 #
javascript
const apiPlugin = {
name: 'api-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.3 带验证的路由 #
javascript
const Joi = require('joi');
const userPlugin = {
name: 'users',
register: async (server, options) => {
server.route({
method: 'POST',
path: '/users',
options: {
validate: {
payload: Joi.object({
name: Joi.string().required(),
email: Joi.string().email().required()
})
}
},
handler: (request, h) => {
return request.payload;
}
});
}
};
三、添加扩展点 #
3.1 请求日志 #
javascript
const loggingPlugin = {
name: 'request-logger',
register: async (server, options) => {
server.ext('onRequest', (request, h) => {
console.log(`[${new Date().toISOString()}] ${request.method.toUpperCase()} ${request.path}`);
return h.continue;
});
}
};
3.2 响应处理 #
javascript
const responsePlugin = {
name: 'response-wrapper',
register: async (server, options) => {
server.ext('onPreResponse', (request, h) => {
const response = request.response;
if (!response.isBoom && !response.source) {
response.source = {
success: true,
data: response.source
};
}
return h.continue;
});
}
};
3.3 认证检查 #
javascript
const authCheckPlugin = {
name: 'auth-check',
register: async (server, options) => {
server.ext('onPostAuth', (request, h) => {
if (request.route.settings.auth !== false) {
if (!request.auth.isAuthenticated) {
throw Boom.unauthorized('Please login');
}
}
return h.continue;
});
}
};
四、添加方法 #
4.1 服务器方法 #
javascript
const methodsPlugin = {
name: 'methods',
register: async (server, options) => {
server.method('formatDate', (date) => {
return new Date(date).toISOString();
});
server.method('generateId', () => {
return Date.now().toString(36);
});
}
};
4.2 带缓存的方法 #
javascript
const cachePlugin = {
name: 'cached-methods',
register: async (server, options) => {
server.method('getUser', async (id) => {
return await User.findById(id);
}, {
cache: {
expiresIn: 60 * 1000,
generateTimeout: 100
}
});
}
};
4.3 方法对象 #
javascript
const utilsPlugin = {
name: 'utils',
register: async (server, options) => {
server.method({
name: 'utils.formatDate',
method: (date) => new Date(date).toISOString()
});
server.method({
name: 'utils.generateId',
method: () => Date.now().toString(36)
});
}
};
五、添加装饰 #
5.1 服务器装饰 #
javascript
const decoratorsPlugin = {
name: 'server-decorators',
register: async (server, options) => {
server.decorate('server', 'success', function (data) {
return {
success: true,
data: data
};
});
server.decorate('server', 'error', function (message) {
return {
success: false,
error: message
};
});
}
};
5.2 请求装饰 #
javascript
const requestDecoratorsPlugin = {
name: 'request-decorators',
register: async (server, options) => {
server.decorate('request', 'getClientIP', function () {
return this.info.remoteAddress;
});
server.decorate('request', 'getUser', function () {
return this.auth.credentials;
});
}
};
5.3 响应装饰 #
javascript
const responseDecoratorsPlugin = {
name: 'response-decorators',
register: async (server, options) => {
server.decorate('response', 'setCustomHeader', function (value) {
return this.header('X-Custom', value);
});
}
};
六、认证策略 #
6.1 自定义认证 #
javascript
const customAuthPlugin = {
name: 'custom-auth',
register: async (server, options) => {
server.auth.scheme('custom', (server, options) => {
return {
authenticate: async (request, h) => {
const token = request.headers.authorization;
if (!token) {
throw Boom.unauthorized('Missing token');
}
try {
const decoded = verifyToken(token, options.secret);
return h.authenticated({ credentials: decoded });
} catch (error) {
throw Boom.unauthorized('Invalid token');
}
}
};
});
server.auth.strategy('default', 'custom', { secret: options.secret });
}
};
6.2 JWT认证 #
javascript
const jwt = require('jsonwebtoken');
const jwtAuthPlugin = {
name: 'jwt-auth',
register: async (server, options) => {
server.auth.scheme('jwt', (server, schemeOptions) => {
return {
authenticate: async (request, h) => {
const authHeader = request.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
throw Boom.unauthorized('Missing or invalid token');
}
const token = authHeader.substring(7);
try {
const decoded = jwt.verify(token, schemeOptions.secret);
return h.authenticated({ credentials: decoded });
} catch (error) {
throw Boom.unauthorized('Invalid token');
}
}
};
});
server.auth.strategy('jwt', 'jwt', { secret: options.secret });
server.auth.default('jwt');
}
};
七、状态管理 #
7.1 使用server.app #
javascript
const statePlugin = {
name: 'state',
register: async (server, options) => {
server.app.config = options;
server.app.startTime = Date.now();
server.app.connections = new Map();
}
};
7.2 使用server.plugins #
javascript
const dataPlugin = {
name: 'data',
register: async (server, options) => {
server.plugins.data = {
users: new Map(),
sessions: new Map()
};
}
};
八、事件处理 #
8.1 服务器事件 #
javascript
const eventsPlugin = {
name: 'events',
register: async (server, options) => {
server.events.on('start', () => {
console.log('Server started');
});
server.events.on('stop', () => {
console.log('Server stopped');
});
server.events.on('response', (request) => {
console.log(`Request completed: ${request.path}`);
});
}
};
8.2 自定义事件 #
javascript
const customEventsPlugin = {
name: 'custom-events',
register: async (server, options) => {
server.event('userCreated');
server.event('userDeleted');
server.events.on('userCreated', (user) => {
console.log('User created:', user.id);
});
}
};
九、完整插件示例 #
9.1 数据库插件 #
javascript
const { Pool } = require('pg');
const databasePlugin = {
name: 'database',
version: '1.0.0',
register: async (server, options) => {
const pool = new Pool({
host: options.host || 'localhost',
port: options.port || 5432,
database: options.database,
user: options.user,
password: options.password
});
server.decorate('server', 'db', pool);
server.method('db.query', async (sql, params) => {
const result = await pool.query(sql, params);
return result.rows;
});
server.events.on('stop', () => {
pool.end();
});
}
};
module.exports = databasePlugin;
9.2 缓存插件 #
javascript
const Redis = require('ioredis');
const cachePlugin = {
name: 'cache',
version: '1.0.0',
register: async (server, options) => {
const redis = new Redis({
host: options.host || 'localhost',
port: options.port || 6379
});
server.decorate('server', 'cache', redis);
server.method('cache.get', async (key) => {
const data = await redis.get(key);
return data ? JSON.parse(data) : null;
});
server.method('cache.set', async (key, value, ttl = 3600) => {
await redis.setex(key, ttl, JSON.stringify(value));
});
server.events.on('stop', () => {
redis.quit();
});
}
};
module.exports = cachePlugin;
9.3 日志插件 #
javascript
const fs = require('fs');
const path = require('path');
const loggerPlugin = {
name: 'logger',
version: '1.0.0',
register: async (server, options) => {
const logFile = fs.createWriteStream(
path.join(options.logDir || 'logs', 'access.log'),
{ flags: 'a' }
);
server.ext('onRequest', (request, h) => {
request.startTime = Date.now();
return h.continue;
});
server.events.on('response', (request) => {
const log = {
timestamp: new Date().toISOString(),
method: request.method,
path: request.path,
query: request.query,
status: request.response.statusCode,
duration: Date.now() - request.startTime,
ip: request.info.remoteAddress
};
logFile.write(JSON.stringify(log) + '\n');
});
server.events.on('stop', () => {
logFile.end();
});
}
};
module.exports = loggerPlugin;
十、插件测试 #
10.1 基本测试 #
javascript
const Hapi = require('@hapi/hapi');
const myPlugin = require('./my-plugin');
describe('My Plugin', () => {
let server;
beforeEach(async () => {
server = Hapi.server({ port: 3000 });
await server.register({
plugin: myPlugin,
options: { setting: 'value' }
});
});
test('should register successfully', () => {
expect(server.registrations['my-plugin']).toBeDefined();
});
test('should add routes', async () => {
const response = await server.inject({
method: 'GET',
path: '/hello'
});
expect(response.statusCode).toBe(200);
});
});
十一、总结 #
编写插件要点:
| 功能 | 方法 |
|---|---|
| 添加路由 | server.route() |
| 添加扩展点 | server.ext() |
| 添加方法 | server.method() |
| 添加装饰 | server.decorate() |
| 添加认证 | server.auth.scheme() |
下一步,让我们学习常用插件!
最后更新:2026-03-28