Express-WX is an Express Router used for building Wechat (an instant messaging software, especially popular in China) Offical Account Message Server easily, supporting loading request (message) handlers dynamically.
Express-WX是一个动态的微信后端组件。它的主体是一个Express的Router,可以像一般的Router一样注册在app的某一个请求路径下。
按照下述示例,构建Express的app对象,并在其上use我们的WXRouter类的实例,设置好token,部署到服务器上。
之后在微信公众平台的开发——基本配置里填入该请求路径对应的url并填入上面设置的token,即可完成服务器搭建。
Express-WX最独特、最令人称赞一点是采用了动态路由架构。只需要在一个指定的文件夹里面放置您的含有消息处理逻辑的.js文件,这个文件就会在运行时被自动加载并调用其中的消息处理逻辑,完成您的需求。
如果想新增、修改或删除处理逻辑,只需要直接对含有上述处理逻辑的文件进行增删改。所有对文件的增删改会实时的应用到程序当中,完全不需要重启应用的麻烦!
此外,您也不用担心会有性能问题。本组件的动态加载处理函数的原理是基于FileWatcher
的,即监听目录内的文件变化、仅当文件产生变化时才会调用相关逻辑动态加载处理函数。因此对于每一次单独的请求,没有任何动态加载的额外开销!
安装:
npm install express-wx --save
您的项目目录结构,应该大致如下图所示:
project
│
├───handlers
│ ├───helloworld.js (helloworld.ts)
│ └───time.js (time.ts)
│
├───app.js
│
└───package.json
注:动态加载不会识别ts文件,因此如果您用Typescript语言,您需要自行用tsc等工具把ts编译为js。
app.js示例:
var Express = require("express")
var Express_WX = require("express-wx")
// 构造Express的实例
var app = Express()
// 构造一个WXRouter的实例(使用express-wx包的WXRouter(config)函数,需要传入config)
var wxRouter = Express_WX.WXRouter({
// config中只有appInfo是必填的,appInfo只有token字段是必填的。
// 如果对更多的config配置感兴趣,请阅读WXRouterConfig接口上的注释(见bld/config.d.ts文件)
appInfo: {
token: "someToken" // 您需要保证在微信公众平台设置的Token和这里设置的Token一致
},
debugToken: "test"
})
// 把wxRouter对象注册到app上
app.use("/", wxRouter)
app.listen(8080) // 监听8080端口
接下来,您就可以撰写自己的请求处理逻辑的.js文件,并把它们放进handlers目录中。为您提供两个handler的示例供参考:
handlers/helloworld.ts示例:(注意需要先编译为js才可使用)
import {TextWXMessage, WXHandler} from "express-wx";
// 向WXHandler(fn)函数传入一个参数,参数的形式即为标准的express的(req, res, next)形式。
// 事实上您不使用WXHandler函数,而是直接提供一个普通函数也是可以的;但特别是当您使用TypeScript时还是强烈建议使用WXHandler,
// 因为这样可以获得更充分的类型提示。
export default WXHandler((req, res, next) => {
// req是WXRequest类型的对象,它除了具有一般的express请求对象的接口以外,还额外有wxRouter属性(即当前WXRouter的实例,
// 您可以从中获得公众号信息),
// 和wx属性(类型为WXMessage,您可以从中获得用户发来的消息。)
// WXMessage有很多种可能的类型、比如文本消息TextMessage类型;其上的text方法即是用户发来的消息文本。
// 下面的语句把收到的消息打印出来。
console.log("收到消息:" + (req.wx as TextWXMessage).text)
//使用res.wx(WXMessage)方法可以回复消息给用户
// 特殊的为了方便,回复文字内容时可以直接使用更简便的res.wxText(string)。
// 下面的语句向用户回复消息"Hello World"。
res.wxText("Hello World")
})
handlers/time.ts示例:(注意需要先编译为js才可使用)
import {TextWXMessage, WXHandler} from "express-wx";
import * as moment from "moment"
// 这个处理逻辑用于在用户发送的文本带有“时间”两字时,向用户回复当前的时间。
var handler = WXHandler((req, res, next) => {
if (req.wx instanceof TextWXMessage && req.wx.text.indexOf("时间") !== -1) {
// 仅当收到的消息是文本消息且文本内容中含有“时间”二字时
// 返回当前日期和时间的格式化字符串,使用moment库
res.wxText(moment(new Date()).format('YYYY-MM-DD HH:mm:ss'))
}
else next() // 否则,则本函数不处理此消息,调用next函数交给后续的人处理。。
})
// 设置一下优先级
// 因为我们有另一个回复一切请求的hellowrorld.js,如果helloworld比这个函数先被调用就无法实现回复当前时间的功能了。
// 一个handler在没有特殊设置的情况下默认优先级为0,因此我们要把上面这个handler的优先级设置为一个大于0的数
handler.priority = 10
export default handler
虽然本程序已经对微信的API进行了尽量简洁、对人友好的封装,但仍然建议您可以阅读一下微信公众平台的官方文档,这样有利于更好的使用本软件实现您的功能。
通过在WXRouter的构造函数传入config,可以实现更多的功能。
请看这里查看更多的配置项参考。
- 用户管理:可以通过对配置项的userProvider函数传入一个(openId: string)=>any类型的函数实现在req上附加user字段、从而在处理逻辑里迅速获知user信息
- 内置WXAPIUserProvider实现,可以直接利用微信官方的API迅速获知用户信息
import {WXRouter, WXAPIUserProvider} from "express-wx";
app.use(WXRouter({
appInfo: {/*...*/},
userProvider: WXAPIUserProvider()
}))
- 日志记录:可以记录到控制台、文件、MongoDB
import {WXRouter, FileOrConsoleLogger, MongoDBLogger} from "express-wx";
app.use(WXRouter({
appInfo: {/*...*/},
logger: new FileOrConsoleLogger("logs/log.txt"), // 不传参则为记录到控制台,传参则为记录到指定路径的文件
messageLogger: new MongoDBLogger("mongodb://localhost:27017/testApp", "testApp") // 记录收到的消息的内容到指定MongoDB的"testApp"collection
}))
- 发送客服消息、模版消息
import {WXRouter, TextWXMessage} from "express-wx";
let wxRouter = WXRouter({
appInfo: {/*...*/},
})
wxRouter.sendCustomMessage("openId", new TextWXMessage("Hello World")) // 发送客服消息
wxRouter.sendCustomTypingStatus("openId", true) // 下发输入状态
console.log(await wxRouter.getAllTemplates()) // 获取所有的添加到公众号的模版信息
await wxRouter.sendTemplateMessage("openId", "template_id", {xxx: "Hello", yyy: {value: "World", "color": "#FF0000"}}, {url: "http://xxx"}) // 发送模版消息
- JSAPI签名
import {WXRouter, TextWXMessage} from "express-wx";
let wxRouter = WXRouter({
appInfo: {/*...*/},
enableJSAPI: true
})
wxRouter.signJSAPI({url: "http://xxx"}) // 内含signature、appid、timestamp、noncestr等字段,直接发回给前端用作wx.config的参数即可。
欢迎提出Issue反馈问题、建议、意见,欢迎发起Pull Request完善本程序。
由于项目尚处于初期阶段,对现有代码的测试尚不完善,因此暂不强制要求您的实现新特性的PR应当编写对新特性的测试。(当然,现有的数量有限的基础测试还是需要通过的,这个会由CI自动完成)
It is very welcomed to raise Issues and/or make Pull Requests. Though English document is not supplied at this time, please feel free to use English in Issues and Pull Requests.
特别感谢Rika的帮助和合作。
本项目的代码中使用了很多她的代码,包括:
- 动态文件加载的代码是基于express-router-dynamic的原理和代码的。
- 部分微信的代码,例如校验请求签名,使用了她的个人未开源代码。
以下是计划在未来逐步实现的功能(按顺序)。欢迎提交Pull Request!
- 素材管理接口
考虑到即使是个人开发者也已经能十分容易的实现https的现状,微信自带的消息加解密功能暂无支持计划。