Loading

企业微信自建应用开发

企业微信自建应用可以让用户开发自己的应用并集成在企业微信里面。我们借助于企业微信的各种接口实现各种个性化的功能,其中一个典型的功能就是要能够接收到用户的输入并根据用户的输入推送相关的信息给用户,本篇博客就来实现这个功能。

准备工作:

1. 在企业微信管理后台创建自建应用并开启API接收功能

并且要对我们开发的Web API接口进行测试,如果通过会显示如下的界面,否则提示“回调错误”

2. 接口测试工具

微信提供了在线接口测试工具,地址:https://open.work.weixin.qq.com/wwopen/devtool/interface/combine。

其中URL是我们部署到服务器的Web接口地址,Token、EncodingAESKey、ToUserName都可以从企业微信获取到,每个企业自建应用都有独立的AgentID,请仔细查看相关文档。这个回调接口测试的是我们的WebAPI接口中的Get请求,如上图所示返回状态“成功”。如果提示“失败”,可能我们开发的Web API接口有错误。

 

这里直接提供测试通过的.NET Core 6.0(VS2022)环境下的WebAPI完整代码供参考。

WebAPI的Controller源码:

[ApiController]
[Route("api/[Controller]")]
public class WXController : ControllerBase
{
    [HttpGet]
    [HttpPost]
    [Route("GetMsg")]
    public string GetMsg(string msg_signature, string timestamp, string nonce, string echostr)
    {
        if (Request.Method.ToUpper().Equals("POST"))
        {
            Request.EnableBuffering();//启动倒带方式 
            byte[] bytes = null;
            string strHtml = string.Empty;
            using (MemoryStream ms = new MemoryStream())
            {
                Request.Body.CopyTo(ms);
                bytes = ms.ToArray();
                strHtml = Encoding.Default.GetString(bytes);
            }

            string token = "xxx"; //自行获取
            string encodingAESKey = "xxx"; //自行获取
            string corpId = "xxx"; //自行获取
            string decryptEchoString = "";

            //接收
            WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(token, encodingAESKey, corpId);
            int ret = wxcpt.DecryptMsg(msg_signature, timestamp, nonce, strHtml, ref decryptEchoString);

            //解析XML
            XmlDocument xml = new XmlDocument();
            xml.LoadXml(decryptEchoString);

            XmlNode toUserNameNode = xml.SelectSingleNode("/xml/ToUserName");
            XmlNode fromUserNameNode = xml.SelectSingleNode("/xml/FromUserName");
            XmlNode createTimeNode = xml.SelectSingleNode("/xml/CreateTime");
            XmlNode msgTypeNode = xml.SelectSingleNode("/xml/MsgType");
            XmlNode contentNode = xml.SelectSingleNode("/xml/Content");
            XmlNode msgIdNode = xml.SelectSingleNode("/xml/MsgId");
            XmlNode agentIDNode = xml.SelectSingleNode("/xml/AgentID");

            string toUserName = toUserNameNode.InnerText.Replace("<![CDATA[", "").Replace("]]>", "");
            string fromUserName = fromUserNameNode.InnerText.Replace("<![CDATA[", "").Replace("]]>", "");
            string createTime = createTimeNode.InnerText.Trim();
            string msgType = msgTypeNode.InnerText.Replace("<![CDATA[", "").Replace("]]>", "");
            string content = contentNode.InnerText.Replace("<![CDATA[", "").Replace("]]>", "");
            string msgId = msgIdNode.InnerText.Replace("<![CDATA[", "").Replace("]]>", "");
            string agentID = agentIDNode.InnerText.Replace("<![CDATA[", "").Replace("]]>", "");

            string msg = "你好!<a href=\"https://www.cnblogs.com/guwei4037\">点这里查看</a>";

            dynamic item = null;
            //文字
            item = new
            {
                touser = fromUserName,
                msgtype = "text",
                agentid = "xxx",//agentid
                text = new
                {
                    content = msg
                },
                safe = "0"
            };

            var body = Newtonsoft.Json.JsonConvert.SerializeObject(item);
            var corpsecret = "xxx"; //每个企业微信自建应用都不同

            string url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=" + corpId + "&corpsecret=" + corpsecret;
            string reval = Utils.HttpUtil.GetData(url);
            dynamic temp = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(reval);
            string tocken = temp["access_token"];

            //获取token,注意有效时间,可用缓存控制
            url = string.Format("https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={0}", tocken);
            string responseData = Utils.HttpUtil.PostData(url, body);
            System.Collections.IDictionary obj = Newtonsoft.Json.JsonConvert.DeserializeObject(responseData) as System.Collections.IDictionary;

            //执行保存到数据库的操作...
            return decryptEchoString;
        }

        if (Request.Method.ToUpper().Equals("GET"))
        {
            string decryptEchoString = "";
            WXBizMsgCrypt wxcpt = new WXBizMsgCrypt("sToken", "sEncodingAESKey", "sCorpID");
            int ret = wxcpt.VerifyURL(msg_signature, timestamp, nonce, echostr, ref decryptEchoString);
            return decryptEchoString;
        }

        return "";
    }
}

里面用到的WXBizMsgCrypt和Cryptography类的源码,企业微信官方网站里面都有下载。官方网址:https://developer.work.weixin.qq.com/document/path/95372。

HttpUtil.cs(我自己写的,比较简单,仅供参考):

查看代码
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;

namespace Utils
{
    public class HttpUtil
    {
        /// <summary>
        /// GET请求数据
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static string GetData(string url)
        {
            string responseData = String.Empty;
            HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url);
            using (HttpWebResponse response = (HttpWebResponse)req.GetResponse())
            {
                using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
                {
                    responseData = reader.ReadToEnd();
                }
            }
            return responseData;
        }

        /// <summary>
        /// POST请求数据
        /// </summary>
        /// <param name="url"></param>
        /// <param name="type">GET or POST</param>
        /// <param name="body"></param>
        /// <returns></returns>
        public static string PostData(string url, dynamic body)
        {
            HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url);
            byte[] bs = Encoding.UTF8.GetBytes(body);
            req.Method = "POST";
            req.ContentType = "application/x-www-form-urlencoded";
            req.ContentLength = bs.Length;

            string responseData = String.Empty;

            using (Stream stream = req.GetRequestStream())
            {
                stream.Write(bs, 0, bs.Length);
            }

            using (HttpWebResponse response = (HttpWebResponse)req.GetResponse())
            {
                using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
                {
                    responseData = reader.ReadToEnd();
                }
            }

            return responseData;
        }
    }
}

除了上面的3个类,还用到Newtonsoft.Json包,可以通过Nuget获取。

最后在Startup的ConfigureServices方法中,添加如下代码:

查看代码
// If using Kestrel
services.Configure<KestrelServerOptions>(options =>
{
    options.AllowSynchronousIO = true;
});

// If using IIS
services.Configure<IISServerOptions>(options =>
{
    options.AllowSynchronousIO = true;
});

否则会出现"Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true instead"的异常。

将这个Web API发布到服务器的IIS之后,用户可以留言给这个企业微信自建应用,自建应用收到消息后会自动推送我们定义的内容给用户。上面的代码演示了发送文本信息,其它类型的消息可以参考企业微信文档完成,都是类似的代码。

 

参考资料:

1. 接收消息文档 https://developer.work.weixin.qq.com/document/path/90238

2. 发送消息文档 https://developer.work.weixin.qq.com/document/path/90236

3. 加解密库下载与返回码 https://developer.work.weixin.qq.com/document/path/95372

posted @ 2022-03-06 23:49  guwei4037  阅读(2955)  评论(1编辑  收藏  举报