微信后台服务器开发

就要下班了,兵长打开手机,看到弹出的某微信聊天机器人广告便点了进去,于是有了如下故事...

最近兵长在看微信的时候突发奇想的去玩了一下某微信机器人,能够像智能语音助手一下和自己聊天

兵长就在想,这机器人是咋做的,咱们是做服务器开发的,咱用go语言快速实现一下给自己玩玩,实现一个定制化的聊天机器人可好

胖sir听到兵长自言自语的不明所以,便走上前说,咋开始玩起聊天了,不来峡谷游了吗?

上次带你本来是想带你成为winer的,没想到,每一把都是loser,我打算最近收收手,控制一下情绪,找机器人抚慰一下我手上的心灵

你是说微信聊天机器人吗?哪些不都是千篇一律的嘛

那么你能弄个定制化的嘛?把我情绪弄好了,我带你来大乱斗吧

~~(偷笑),小伙子,还好我留了一手,我先给你说说微信后台服务器如何初步开发一个简单的你问我答功能吧,授人以渔,不如授人以鱼是不

开发一个微信后台服务器作为被动回复机器人,大致分为如下几步:

  • 开通公众号,注册微信公众号开发平台,这里可以是注册订阅号,详细的后面给你说
  • 配置权限,配置微信后台开发者权限
  • 流程介绍
  • 接入微信后台
  • 功能实现
  • 兵长乱斗带胖sir飞

开通公众号

注册微信公众号开发平台,这里可以是注册订阅号,按照提示进行注册输入信息即可,相信你一看就会

注册地址:https://mp.weixin.qq.com/cgi-bin/registermidpage?action=index&lang=zh_CN&token=

image

我整理了一个表格来介绍一下订阅号和服务器号的区别,你也可以先初步了解一下区别,后面可以慢慢琢磨

订阅号服务号
用途主要目的 为大家传播咨询;类似报纸,杂志,个人输出偏向企业或组织的交互,如银行,商场,餐厅等;自助服务的服务号,用于企业
群发次数一天可以发送一次;所有的订阅号推送消息,会被统一收纳到订阅号栏目中一个月发送4条群发消息
展示位置全部收录在 订阅号的 信息栏中展示在好友消息列表之中;关注一个服务号,即相当与加了一个朋友
微信支付不可开通支付功能认证后 可以开通微信支付功能
自定义菜单相对简单相对高级,微信有接口,可以自行开发

配置权限

配置微信后台开发者权限

  • 进入公众号管理页面,下拉左边侧,进入基本配置

image

image

  1. URL:填写自己的外网服务器URL,如果没有可以买一个云服务器,现在买云服务器还是很便宜的
  2. Token:自定义Token,用于制作签名,这个非常重要,需要保密
  3. EncodingAESKey:随机生成即可
  4. 消息加解密方式:为了演示方便,我们这里使用明文模式
  • 微信公众号后台接口权限

普通用户只要是接收消息和自动回复消息的权限

image

流程介绍

开发被动回复消息流程介绍,简单来说,可以是这样的

image

功能实现必备知识点

  • http服务进行通信
  • Token机制
  • 微信后台开发xml的数据序列化

http服务

做上述被动回复消息的功能,此处仅需要后台服务器实现get方法和post方法即可

  • get方法

    主要是用于,我们在微信后台设置token的时候,微信后台会向我们的服务器发送get请求,判断我们服务是否有正确的数据

  • post方法

    主要是用于,粉丝在我们的微信后台发送消息的时候,是以post的方式发送给我们的后台服务器的

Token机制

参数描述
signature微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
timestamp时间戳
nonce随机数
echostr随机字符串

开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。加密/校验流程如下:

1)将token、timestamp、nonce三个参数进行字典序排序

2)将三个参数字符串拼接成一个字符串进行sha1加密

3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

token算法流程图

image

验证方法(get)

  • 1.服务器端获取token、nonce、timestamp组成列表
  • 2.列表排成字典序
  • 3.排序后的元素进行摘要
  • 4.摘要比对signature
  • 5.响应echostr

参考微信后台开发文档连接:https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Getting_Started_Guide.html

xml数据解析

兵长,不知道你有没有用过xml来做数据的序列化,此处你肯定会问,为什么用xml,而不用json,不用protobuf

来我给你简单介绍一下xml:

XML是可扩展标记语言,其中标记指的是计算机中所能理解的信息符号,通过标记计算机之间可以处理包含各种信息的资源,我们可以通过通用的标记语言来进行标记

用人话来说就是,xml是数据序列化的其中一种方式,微信定下来使用该方式来对数据进行序列化,我们需要对此进行开发,因此也需要遵循微信的规则

例如 微信后台的 text消息类型 请求 xml格式如下

image

文本消息,微信公众平台请求微信后台服务器会带的字段有:FromUserNameToUserNameCreateTime,MsgTypeContentMsgId

我们开发的微信后台服务器,需要按照如下数据格式做回复:xml 带上如下字段ToUserNameFromUserNameCreateTime,MsgTypeContent

image

兵长,再给你回顾一下,实现微信后台服务器被动回复消息的服务,需要用到上述说到的3点,http服务、token机制、xml解析,记住咯,我要开始撸代码了

具体实现

main.go

  • 启动http服务器,开始监听80端口
package main

import (
   "fmt"
   "github.com/wonderivan/logger"
   "net/http"
   "time"
)

const (
   port     = 80 //后台服务器端口号80,自己的公网服务器 需要设置防火墙,打开80端口
   token    = "XXXXXXX"
)

// 需要自己修改token,以适应自己公众号的token
func main() {

   logger.SetLogger("./log.json")
   logger.Info(" ------------ start  main ------------")

   server := http.Server{
      Addr:           fmt.Sprintf(":%d", port), // 设置监听地址, ip:port
      Handler:        &httpHandler{},           // 具体使用哪个handler来处理
      ReadTimeout:    5 * time.Second,          // 读写超时 微信给出来 5秒
      WriteTimeout:   5 * time.Second,
      MaxHeaderBytes: 0,
   }

   logger.Info(fmt.Sprintf("Listen: %d", port))
   logger.Fatal(server.ListenAndServe())
}

route.go

  • 路由功能 , 具体实现get 和post方法
  • 定义Handler 以及 ServeHTTP 接口的实现
package main

import (
   "fmt"
   "github.com/wonderivan/logger"
   "io"
   "net/http"
   "regexp"
   "time"
   "wechat/wx"
)

type WebController struct {
   Function func(http.ResponseWriter, *http.Request)
   Method   string
   Pattern  string
}

var mux []WebController // 自己定义的路由
// ^ 匹配输入字符串的开始位置
func init() {
   mux = append(mux, WebController{post, "POST", "^/"})
   mux = append(mux, WebController{get, "GET", "^/"})
}

type httpHandler struct{} 

//httpHandler 自己实现了 ServeHTTP 接口,可以通过源码看到,创建httpHandler 对象的时候,实际源码逻辑是会调用ServeHTTP进行处理
func (*httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

   t := time.Now()
   for _, webController := range mux { // 遍历路由
      // 匹配请求的   r.URL.Path  -> webController.Pattern
      if m, _ := regexp.MatchString(webController.Pattern, r.URL.Path); m { // 匹配URL
         logger.Info("webController.Pattern  == ", webController.Pattern)
         logger.Info("r.URL.Path  == ", r.URL.Path)
         if r.Method == webController.Method { // 匹配方法
            logger.Info("webController.Method  == ", webController.Method)

            webController.Function(w, r) // 调用对应的处理函数

            d := time.Now().Sub(t)

            l := fmt.Sprintf("[ACCESS] | % -10s | % -40s | % -16s", r.Method, r.URL.Path, d.String())

            logger.Info(l)

            return
         }
      }
   }

   d := time.Now().Sub(t)

   l := fmt.Sprintf("[ACCESS] | % -10s | % -40s | % -16s", r.Method, r.URL.Path, d.String())

   logger.Info(l)

   io.WriteString(w, "")
   return
}

// 处理token的认证
func get(w http.ResponseWriter, r *http.Request) {

   client, err := wx.NewClient(r, w, token)

   if err != nil {
      logger.Info(err)
      w.WriteHeader(403) // 校验失败
      return
   }

   if len(client.Query.Echostr) > 0 {
      logger.Info("Echostr == ", client.Query.Echostr)
      w.Write([]byte(client.Query.Echostr)) // 校验成功返回的是Echostr
      return
   }

   w.WriteHeader(403)
   return
}

// 微信平台过来消息, 处理 ,然后返回微信平台
func post(w http.ResponseWriter, r *http.Request) {

   client, err := wx.NewClient(r, w, token)

   if err != nil {
      logger.Info(err)
      w.WriteHeader(403)
      return
   }
   // 到这一步签名已经验证通过了
   client.Run()
   return
}

具体代码实现还有1个定义结构文件和核心实现文件:

  • structs.go

    xml序列化消息对应的结构体的定义,包含基本消息字段,FromUserNameToUserNameMsgTypeCreateTime,以及文本消息,图片消息,录音消息,音乐消息,地理位置消息,视频消息等需要哪一些字段,都可以参考微信后台给出的规则

  • wx.go

    token + 随机数 + 时间戳 排成字典序,并使用sha1加密后生成 signature

    NewClient的具体实现

    文本消息,图片消息等的消息处理以及被动回复功能

上述2个核心文件若是感兴趣,自己有想法并且期望自己实现的小伙伴,可以添加我的微信,可以给大家共享一下

当然,微信后台开发涉及的功能还很多,今天给大家分享到的还只是冰山一角,沿途的风景还是需要大家一步一个脚印去感受,可以在微信的开发文档中尽情实战,如下图,感兴趣的可以多多交流。

image

技术是开放的,我们共享技术,心态也应是开放的,我们拥抱变化,你愿意不断摸索前进,你必将收获你期望的东西。

以上是本期全部内容,坚持原创,实践自己的想法,我们下期见

作者:小魔童哪吒


阿兵云原生
192 声望37 粉丝