美文网首页eiYo
使用koa2对接微信公众平台

使用koa2对接微信公众平台

作者: 胖乎乎的萝卜 | 来源:发表于2017-03-13 17:47 被阅读4776次

    这几天心血来潮,想玩下微信公众平台,看看它是如何对接到个人服务器的。这里简单记录下。

    环境:
      node v7.7.1
      koa  v2.0.1
    

    流程

    1.在微信公众平台的 开发 -> 基本配置 那填写服务器信息;

    2.点击启用后,微信会发 Get 请求到填写的服务器,检查服务器是否有效;

    3.服务器验证通过后,公众号每次接收到新消息,都会发 POST 请求到服务器,然后我们就可以在服务器里进行各种处理。

    验证服务器

    微信开发文档里有写验证的规则(这不是废话么):

    verify.png

    上面的文字已经写的很直白了,不多说,代码实现如下:

    // 验证消息来自微信服务器
    const crypto = require('crypto')
    module.exports = (ctx) => {
        const token = 'xxxx', // 自定义,与公众号设置的一致
              signature = ctx.query.signature,
              timestamp = ctx.query.timestamp,
              nonce = ctx.query.nonce
            
        // 字典排序
        const arr = [token, timestamp, nonce].sort()
    
        const sha1 = crypto.createHash('sha1')
        sha1.update(arr.join(''))
        const result = sha1.digest('hex')
    
        if (result === signature) {
            ctx.body = ctx.query.echostr
        } else {
            ctx.body = { code: -1, msg: "fail"}
        }
    }
    

    处理 POST 请求

    下面要处理接收消息的 POST 请求。因为消息的格式都是 XML ,所以这里需要引入 xml2js 。

    接收 XML 数据

    koa2 没有对 XML 格式的参数进行处理,这里需要我们自己来处理下,写的中间件如下:

    // xmlTool.js
    const xml2js = require('xml2js')
    
    exports.xmlToJson = (str) => {
         return new Promise((resolve, reject) => {
            const parseString = xml2js.parseString
            parseString(str, (err, result) => {
                if (err) {
                    reject(err)
                } else {
                    resolve(result)
                }
            })
         })
    }
    
    exports.jsonToXml = (obj) => {
        const builder = new xml2js.Builder()
        return builder.buildObject(obj)
    }
    
    // xmlParse.js
    const xml = require('./xmlTool')
    
    module.exports = () => {
        return async (ctx, next) => {
            if (ctx.method == 'POST' && ctx.is('text/xml')) {
                let promise = new Promise(function (resolve, reject) {
                    let buf = ''
                    ctx.req.setEncoding('utf8')
                    ctx.req.on('data', (chunk) => {
                        buf += chunk
                    })
                    ctx.req.on('end', () => {
                        xml.xmlToJson(buf)
                            .then(resolve)
                            .catch(reject)
                    })
                })
    
                await promise.then((result) => {
                        ctx.req.body = result
                    })
                    .catch((e) => {
                        e.status = 400
                    })
    
                next()
            } else {
                await next()
            }
        }
    }
    
    

    加上了这个中间件,我们就可以正确接收到 XML 格式的参数了。接收到的参数如下:

    // console.log(buf) 
    <xml>\n<ToUserName><![CDATA[toUser]]></ToUserName>\n<FromUserName><![CDATA[fromUser]]></FromUserName>\n<C
    reateTime>12345678</CreateTime>\n<MsgType><![CDATA[text]]></MsgType>\n<Content><![CDATA[你好]]></Content>\n</x
    ml>\n\n
    
    // 转为JSON后
    { ToUserName: [ 'toUser' ],
      FromUserName: [ 'fromUser' ],
      CreateTime: [ '12345678' ],
      MsgType: [ 'text' ],
      Content: [ '你好' ] }
    

    发送消息

    接收到消息后,服务器需要在5s内返回消息,如果没内容返回,可以返回 success 或空字符串。下面是返回文本信息的例子:

    // wx.js
    exports.message = {
        text (msg, content) {
            return xml.jsonToXml({
                xml: {
                    ToUserName: msg.FromUserName,
                    FromUserName: msg.ToUserName,
                    CreateTime: Date.now(),
                    MsgType: msg.MsgType,
                    Content: content
                }
            })
        }
    }
    
    // index.js
    const wx = require('./wx')
    exports.postHandle = (ctx, next) => {
        let msg,
            MsgType,
            result
    
        msg = ctx.req.body ? ctx.req.body.xml : ''
    
        if (!msg) {
            ctx.body = 'error request.'
            return;
        }
        
        MsgType = msg.MsgType[0]
    
        switch (MsgType) {
            case 'text':
                result = wx.message.text(msg, msg.Content)
                break;
            default: 
                result = 'success'
        }
        ctx.res.setHeader('Content-Type', 'application/xml')
        ctx.res.end(result)
    }
    

    这样子就可以返回文本信息啦~到这里已经把基本的流程走完。其他更多的操作可以看官方文档,加以修改就好。

    公众号例子如下,目前只是实现了回复相同的文本内容:

    qrcode_430.jpg

    上面的代码已开源到https://github.com/cirplan/koa2-wechat 上,欢迎围观。

    相关文章

      网友评论

      • Zzzz_dd17:我用EGG.js 写的 也是做的是 转发客服 也是要返回XML 格式的数据 但是微信上一直报错 说该公众号服务器出现故障
        胖乎乎的萝卜:这种一般是微信发的请求得不到正确响应~
      • 是龙也要趴着:我看你转换成json后值是数组,能否转成对象?
        是龙也要趴着:xml2json这个库不好用,调用C++库
        胖乎乎的萝卜:@龙LONG显林 需要的话直接用xml2json替换就好
        是龙也要趴着:xml2json用这个库重写一下文章吧
      • 读书的实习者:谢谢博主的分享,如果返回文本信息需要调用别的接口拿到相应的结果才能返回这个文本信息可以实现吗
        胖乎乎的萝卜:可以的哦,如果接口在5s内返回,直接拿了数据返回即可。
        如果接口5s内不能返回,则要先响应。具体看文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140543

        假如服务器无法保证在五秒内处理并回复,必须做出下述回复,这样微信服务器才不会对此作任何处理,并且不会发起重试(这种情况下,可以使用客服消息接口进行异步回复)

      • 5ec66e575529:博主,菜鸟问个问题,为什么获取xml数据要监听data事件才能获取呢(ctx.req.on('data')).ctx.request.body怎么获取不了,是不是bodyPaser中间件不支持?求解答
        5ec66e575529:@哈Q士 原来如此,感谢~
        胖乎乎的萝卜:koa对xml参数是没有处理的,express也是,都要另外加中间件

      本文标题:使用koa2对接微信公众平台

      本文链接:https://www.haomeiwen.com/subject/fglqnttx.html