🎯 学习目标:学会自定义扩展:插件开发、中间件、钩子函数
⏱️ 阅读时间:约 12 分钟
💡 前置要求:无
🔌 插件开发
插件结构
my-plugin/├── index.js # 插件入口├── package.json # 依赖配置├── plugin.json # 插件元数据└── README.md # 使用说明插件元数据
{"name":"my-custom-plugin","version":"1.0.0","description":"我的自定义插件","author":"你的名字","main":"index.js","engines":{"openclaw":">=1.0.0"},"hooks":["beforeRequest","afterResponse","onError"]}插件入口
// index.jsmodule.exports = {name: 'my-custom-plugin',version: '1.0.0',// 插件初始化asyncinit(context) {console.log('插件初始化', context);// 注册钩子 context.registerHook('beforeRequest', this.beforeRequest); context.registerHook('afterResponse', this.afterResponse);// 注册命令 context.registerCommand('mycmd', this.handleCommand);// 注册技能 context.registerSkill('my-skill', this.mySkill); },// 请求前钩子asyncbeforeRequest(request) {console.log('收到请求:', request);// 可以修改请求 request.headers['X-Custom-Header'] = 'MyPlugin';return request; },// 响应后钩子asyncafterResponse(response) {console.log('返回响应:', response);// 可以修改响应 response.metadata.processedBy = 'my-plugin';return response; },// 命令处理asynchandleCommand(args) {return`执行自定义命令:${args.join(' ')}`; },// 自定义技能asyncmySkill(params) {return`技能执行结果:${params.input}`; },// 插件卸载asyncdestroy() {console.log('插件卸载'); }};🔄 中间件
中间件模式
classMiddlewareManager {constructor() {this.middlewares = []; }use(middleware) {this.middlewares.push(middleware); }asyncexecute(context) {let index = 0;constnext = async () => {if (index >= this.middlewares.length) {return context; }const middleware = this.middlewares[index++];returnawaitmiddleware(context, next); };returnawaitnext(); }}// 使用示例const manager = newMiddlewareManager();// 日志中间件manager.use(async (context, next) => {console.log('请求开始:', context.request.id);const result = awaitnext();console.log('请求结束:', context.request.id);return result;});// 认证中间件manager.use(async (context, next) => {if (!context.request.token) {thrownewError('未认证'); }const user = awaitvalidateToken(context.request.token); context.user = user;returnawaitnext();});// 限流中间件manager.use(async (context, next) => {const rateLimit = awaitcheckRateLimit(context.user.id);if (rateLimit.exceeded) {thrownewError('请求过于频繁'); }returnawaitnext();});// 执行const result = await manager.execute({request: { id: 1, token: 'xxx' }});实用中间件
1. 认证中间件
functionauthMiddleware(options = {}) {returnasync (context, next) => {const token = context.request.headers.authorization;if (!token) {if (options.optional) {returnawaitnext(); }thrownewError('缺少认证令牌'); }try {const user = awaitverifyToken(token.replace('Bearer ', '')); context.user = user; } catch (error) {thrownewError('认证失败'); }returnawaitnext(); };}2. 日志中间件
functionloggingMiddleware() {returnasync (context, next) => {const start = Date.now();console.log(`[请求] ${context.request.method}${context.request.path}`);try {const result = awaitnext();const duration = Date.now() - start;console.log(`[响应] ${duration}ms`);return result; } catch (error) {const duration = Date.now() - start;console.error(`[错误] ${duration}ms - ${error.message}`);throw error; } };}3. 错误处理中间件
functionerrorMiddleware() {returnasync (context, next) => {try {returnawaitnext(); } catch (error) {console.error('未捕获错误:', error);// 记录错误awaitlogError(error, context);// 返回友好错误信息return {success: false,error: {message: error.message,code: error.code || 'INTERNAL_ERROR' } }; } };}🎣 钩子函数
生命周期钩子
const hooks = {// 请求前beforeRequest: [],// 请求后afterResponse: [],// 错误处理onError: [],// 技能执行前beforeSkillExecute: [],// 技能执行后afterSkillExecute: [],// 系统启动onStartup: [],// 系统关闭onShutdown: []};classHookManager {register(hookName, callback, priority = 0) {if (!hooks[hookName]) {thrownewError(`未知钩子:${hookName}`); } hooks[hookName].push({ callback, priority }); hooks[hookName].sort((a, b) => b.priority - a.priority); }asyncexecute(hookName, context) {const hookCallbacks = hooks[hookName] || [];for (const { callback } of hookCallbacks) {awaitcallback(context); } }}// 使用示例const hookManager = newHookManager();// 注册钩子hookManager.register('beforeRequest', async (context) => {console.log('请求前处理');});hookManager.register('afterResponse', async (context) => {console.log('响应后处理');});// 触发钩子await hookManager.execute('beforeRequest', { request });实用钩子示例
1. 审计日志钩子
hookManager.register('beforeRequest', async (context) => {await auditLogger.log({type: 'request',userId: context.user?.id,path: context.request.path,timestamp: newDate() });});2. 性能监控钩子
hookManager.register('beforeRequest', async (context) => { context.startTime = Date.now();});hookManager.register('afterResponse', async (context) => {const duration = Date.now() - context.startTime;await performanceMonitor.record('request_duration', duration);});3. 速率限制钩子
hookManager.register('beforeRequest', async (context) => {const userId = context.user?.id;if (userId) {const exceeded = awaitcheckRateLimit(userId);if (exceeded) {thrownewError('请求过于频繁,请稍后重试'); } }});🧩 扩展示例
示例 1:自定义认证提供者
module.exports = {name: 'custom-auth-provider',asyncinit(context) {// 注册自定义认证 context.registerAuthProvider('ldap', async (credentials) => {returnawaitauthenticateWithLDAP(credentials); }); context.registerAuthProvider('oauth2', async (code) => {returnawaitauthenticateWithOAuth2(code); }); }};示例 2:消息格式化插件
module.exports = {name: 'message-formatter',asyncinit(context) { context.registerHook('afterResponse', async (context) => {// 将响应格式化为 Markdownif (context.request.format === 'markdown') { context.response.content = toMarkdown(context.response.content); }// 添加表情符号if (context.request.addEmoji) { context.response.content = addEmojis(context.response.content); } }); }};示例 3:多语言支持插件
module.exports = {name: 'i18n-support',asyncinit(context) {// 加载翻译文件this.translations = awaitloadTranslations(); context.registerHook('beforeRequest', async (context) => {// 检测语言const lang = context.request.headers['accept-language'] || 'zh'; context.language = lang; }); context.registerHook('afterResponse', async (context) => {// 翻译响应if (context.language !== 'zh') { context.response.content = awaittranslate( context.response.content,'zh', context.language ); } }); }};✅ 学完这篇你能做什么
学完 Day 25,你将能够:
✅ 开发自定义插件 ✅ 配置中间件 ✅ 使用钩子函数 ✅ 扩展 OpenClaw 功能 ✅ 处理扩展相关错误
🔜 下篇预告
Day 26:部署方案:云服务器、容器编排、高可用架构
☁️ 云服务器选择 🐳 容器化部署 🏗️ 高可用架构
💬 互动环节
你想开发什么自定义插件?留言分享!
公众号:OpenClaw 研习社
系列:OpenClaw 30 天入门到精通
作者:OpenClaw 研习社
夜雨聆风