乐于分享
好东西不私藏

OpenClaw 30 天系列 - Day 25:自定义扩展:插件开发、中间件、钩子函数

OpenClaw 30 天系列 - Day 25:自定义扩展:插件开发、中间件、钩子函数

🎯 学习目标:学会自定义扩展:插件开发、中间件、钩子函数

⏱️ 阅读时间:约 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: { id1token'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 {successfalse,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,timestampnewDate()  });});

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 研习社