插件概念 #

一、什么是插件? #

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