第一部分:基础配置
第一步:注册微信公众账号
如果开发测试阶段可以打开测试链接地址,注册测试公众号。测试账号除了不能与正式账号通信外其他什么高级接口的都可以实现。
测试号管理地址:http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
账号开通成功后系统会分配两个账号信息:appID、appsecret作为此账号的标识。
第二步:接口配置信息
这里的接口配置主要为保证微信能与你所建的站点进行数据交互,并且数据的传递要符合规则。
这里需要完成两个参数配置:
URL:你可以在自己的项目中新建一个action或者其他在微信访问此地址时能返回数据就行。
Token:一个建立两者对话的口令,设置完成后项目中也需要配置同样的Token方可正常通信。
这里你可以在定义的URL中直接返回微信的请求“echostr”参数值,当然正常情况下是需要按照规则验证数据的请求安全后返回。
可以在请求的URL指定的Action中按照微信官方提示,做如下签名验证(这样做属于安全的):
public ActionResult Webcatch() { string token = "loyung"; if (string.IsNullOrWhiteSpace(token)) { return null; } string echoStr = Request.QueryString["echoStr"];//随机字符串 string signature = Request.QueryString["signature"];//微信加密签名 string timestamp = Request.QueryString["timestamp"];//时间戳 string nonce = Request.QueryString["nonce"];//随机数 string[] ArrTmp = { token, timestamp, nonce }; Array.Sort(ArrTmp); //字典排序 string tmpStr = string.Join("", ArrTmp); tmpStr = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(tmpStr, "SHA1"); tmpStr = tmpStr.ToLower(); if (tmpStr == signature) { return Content(echoStr); } else { return Content("false"); } }
第三步:JS接口安全域名配置
按照页面的提示,配置上需要做微信接口的域名配置。
第二部分:关于微信操作类的封装
以上第一部分为入门级的简单操作,如果要把微信的整个接口开发完,当然要思考怎样把微信的每个接口无缝对接到现有系统。
思路:1.将接口的常用返回值作为一种类型去接收。
2.微信接口都可以使用GET请求,可以封装Get数据请求处理工厂。
3.最重要的莫过于每次请求都需要有效的access_token,由于access_token的有效期只有两个小时,这里我们采用文件的形式记录access_token,并在每次使用前检查access_token是否可用。
4.代码文件:Model WeiXinModel.cs存放数据要被序列化的实体类结构,Control WeChat.cs用来封装所有接口的调用以及对access_token的处理。View 直接调用WeChat封装接口。
由于所有接口实在太多,这里做一个获取所有关注粉丝信息的Demo。
Model WeiXinModel.cs
/* * 创建时间:2016-07-19 * 创建人:刘自洋 * 说明:此文件下包含所有微信实体类 */ using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace Ecio_Admin.Models { #region 系统返回信息 /// <summary> /// 系统返回信息 /// </summary> /// <typeparam name="T">指定返回类型</typeparam> public class wx_backdata<T> { /// <summary> /// 接口返回数据状态true成功|false失败 /// </summary> public bool ResponseState; /// <summary> /// 接口返回正确数据 /// </summary> public T ResponseData; /// <summary> /// 接口返回错误时间 /// </summary> public wx_apperror ErrorData; } #endregion #region 微信接口返回错误类 /// <summary> /// 微信接口返回错误类 /// </summary> public class wx_apperror { /// <summary> /// 接口错误代码 /// </summary> public string errcode; /// <summary> /// 接口错误消息 /// </summary> public string errmsg; } #endregion #region 获取接口返回Token /// <summary> /// 获取接口返回凭证Token /// </summary> public class wx_access_token { /// <summary> /// 获取到的凭证 /// </summary> public string access_token; /// <summary> /// 凭证有效时间,单位:秒 /// </summary> public string expires_in; } #endregion #region 获取此用户OpenID /// <summary> /// 获取用户OpenID列表 /// </summary> public class wx_openidlist { /// <summary> /// 关注该公众账号的总用户数 /// </summary> public string total; /// <summary> /// 拉取的OPENID个数,最大值为10000 /// </summary> public string count; /// <summary> /// 列表数据,OPENID的列表 /// </summary> public data data; /// <summary> /// 拉取列表的最后一个用户的OPENID /// </summary> public string next_openid; } /// <summary> /// openid对象 /// </summary> public class data { /// <summary> /// 用户标识 /// </summary> public List<string> openid; } #endregion #region 获取用户个人信息 public class wx_user_info { /// <summary> /// 是否订阅该公众号0没有关注|1已关注 /// </summary> public string subscribe; /// <summary> /// 用户的标识,对当前公众号唯一 /// </summary> public string openid; /// <summary> /// 用户的昵称 /// </summary> public string nickname; /// <summary> /// 用户的性别0未知|1男性|2女性 /// </summary> public string sex; /// <summary> /// 用户所在城市 /// </summary> public string city; /// <summary> /// 用户所在国家 /// </summary> public string country; /// <summary> /// 用户所在省份 /// </summary> public string province; /// <summary> /// 用户的语言 /// </summary> public string language; /// <summary> /// 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像) /// </summary> public string headimgurl; /// <summary> /// 用户关注时间 /// </summary> public string subscribe_time; /// <summary> /// 绑定到微信开放平台唯一标识 /// </summary> public string unionid; /// <summary> /// 粉丝备注 /// </summary> public string remark; /// <summary> /// 用户所在的分组ID(兼容旧的用户分组接口) /// </summary> public string groupid; /// <summary> /// 用户被打上的标签ID列表 /// </summary> public List<string> tagid_list; } #endregion }
Control WeChat.cs
/* * 创建时间:2016-07-18 * 创建人:刘自洋 * 说明:此类包含微信相关配置,接口凭据获取、更新,以及常用接口调用。 */ using Ecio_Admin.Models; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Xml; namespace Ecio_Admin.Common { /// <summary> /// 微信配置、操作类 /// </summary> public class WeChat { #region 【构造】微信配置 /// <summary> /// 初始化微信参数配置 /// </summary> public WeChat() { XmlDocument Xml = new XmlDocument(); Xml.Load(HttpContext.Current.Server.MapPath("~/App_Data/Config/WeiXin.config")); XmlNode appid = Xml.SelectSingleNode("weixin/appid"); XmlNode appsecret = Xml.SelectSingleNode("weixin/appsecret"); XmlNode accesstoken = Xml.SelectSingleNode("weixin/accesstoken"); XmlNode token = Xml.SelectSingleNode("weixin/token"); if (appid != null) { this.appid = appid.InnerText; } if (appsecret != null) { this.secret = appsecret.InnerText; } if (accesstoken != null) { if (check_access_token()) { this.accesstoken = accesstoken.InnerText; } else { var backdate = get_access_token(); //更新当前access_token if (backdate.ResponseState) { this.accesstoken = backdate.ResponseData.access_token; accesstoken.InnerText = backdate.ResponseData.access_token; Xml.Save(HttpContext.Current.Server.MapPath("~/App_Data/Config/WeiXin.config")); } } } if (token != null) { this.token = token.InnerText; } } #endregion #region 【配置】微信基础参数 /// <summary> /// 服务器标识 /// </summary> private string token = "loyung"; /// <summary> /// 开发者ID(AppID(应用ID)) /// </summary> private string appid = "wx**********"; /// <summary> /// (AppSecret(应用密钥)) /// </summary> private string secret = "************ "; /// <summary> ///接口连接凭据 /// </summary> private string accesstoken = ""; #endregion #region 【验证】微信签名 /// <summary> /// 微信签名验证 /// </summary> /// <param name="nonce">随机字符串 </param> /// <param name="timestamp">时间戳 </param> /// <param name="signature">微信加密签名</param> /// <returns>验签是否成功true成功|false失败</returns> public bool CheckSign(string nonce, string timestamp, string signature) { string[] ArrTmp = { token, timestamp, nonce }; Array.Sort(ArrTmp); //字典排序 string tmpStr = string.Join("", ArrTmp); tmpStr = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(tmpStr, "SHA1"); tmpStr = tmpStr.ToLower(); if (tmpStr == signature) { return true; } else { return false; } } #endregion #region 【验证】access_token是否失效 /// <summary> /// 根据接口返回代码42001验证是否可用 /// </summary> /// <returns>true可用|false失效</returns> public bool check_access_token() { XmlDocument Xml = new XmlDocument(); Xml.Load(HttpContext.Current.Server.MapPath("~/App_Data/Config/WeiXin.config")); XmlNode access_token = Xml.SelectSingleNode("weixin/accesstoken"); if (access_token != null) { this.accesstoken = access_token.InnerText; } string Url = "https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token=" + this.accesstoken; string GetResult = ToolKit.GetData(Url); if (GetResult.IndexOf("errcode") != -1) { var ErrorMessage = JsonConvert.DeserializeObject<wx_apperror>(GetResult); if (ErrorMessage.errcode == "42001") { return false; } return false; } else { return true; } } #endregion #region 【通用】获取可用接口凭据 /// <summary> /// 获取可用的接口凭据 /// </summary> public string Accesstoken() { if (!check_access_token()) { XmlDocument Xml = new XmlDocument(); Xml.Load(HttpContext.Current.Server.MapPath("~/App_Data/Config/WeiXin.config")); XmlNode access_token = Xml.SelectSingleNode("weixin/accesstoken"); if (access_token != null) { var backdate = get_access_token(); //更新当前access_token if (backdate.ResponseState) { this.accesstoken = backdate.ResponseData.access_token; access_token.InnerText = backdate.ResponseData.access_token; Xml.Save(HttpContext.Current.Server.MapPath("~/App_Data/Config/WeiXin.config")); } } } return this.accesstoken; } #endregion #region【通用】获取JSON指定类型返回值 /// <summary> /// 【通用】获取JSON指定类型返回值 /// </summary> /// <typeparam name="T">指定非错误的返回类型</typeparam> /// <param name="JsonData">序列化前JSON字符</param> /// <returns>JSON序列化后数据</returns> public wx_backdata<T> GetJson<T>(string JsonData) { var result = new wx_backdata<T>(); if (JsonData.IndexOf("errcode") != -1) { result.ResponseState = false; result.ErrorData = JsonConvert.DeserializeObject<wx_apperror>(JsonData); } else { result.ResponseState = true; result.ResponseData = JsonConvert.DeserializeObject<T>(JsonData); } return result; } #endregion #region 【access_token】获取唯一接口调用凭据 /// <summary> /// 获取唯一接口调用凭据access_token /// </summary> /// <returns>access_token获取结果对象</returns> public wx_backdata<wx_access_token> get_access_token() { string Url = "https://api.weixin.qq.com/cgi-bin/token?"; string grant_type = "client_credential"; string GetResult = ToolKit.GetData(Url + "grant_type=" + grant_type + "&appid=" + appid + "&secret=" + secret); return GetJson<wx_access_token>(GetResult); } #endregion #region【OpenId】 获取用户列表 /// <summary> /// 获取粉丝微信OpenId /// </summary> /// <returns>接口返回值</returns> public wx_backdata<wx_openidlist> GetOpenIdList() { string Url = "https://api.weixin.qq.com/cgi-bin/user/get?"; string GetResult = ToolKit.GetData(Url + "access_token=" + this.Accesstoken() + "&next_openid="); return GetJson<wx_openidlist>(GetResult); } #endregion #region 【用户信息】获取指定用户信息 /// <summary> /// 获取指定用户信息 /// </summary> /// <param name="openid">用户标识</param> /// <returns>接口返回值</returns> public wx_backdata<wx_user_info> GetUserInfo(string openid) { string Url = "https://api.weixin.qq.com/cgi-bin/user/info?"; string GetResult = ToolKit.GetData(Url + "access_token=" + this.Accesstoken() + "&openid=" + openid + "&lang=zh_CN"); return GetJson<wx_user_info>(GetResult); } #endregion } }
View UserList.cshtm
public ActionResult GetUserList(int? page, int? take) { List<wx_user_info> Users = new List<wx_user_info>(); var OpenList = new WeChat().GetOpenIdList(); if (OpenList.ResponseState) { foreach (var item in OpenList.ResponseData.data.openid) { var backdata = new WeChat().GetUserInfo(item); if (backdata.ResponseState) { Users.Add(backdata.ResponseData); } } ViewBag.Tip = OpenList.ResponseData.count + "人"; } else { ViewBag.Tip = "系统异常,编号:" + OpenList.ErrorData.errcode + "错误信息:" + OpenList.ErrorData.errmsg; } var PageUseras = Users.Where(ua=>true); return View(PageUseras); }
File WeiXin.config
<?xml version="1.0" encoding="utf-8"?> <weixin> <appid>wx**********</appid> <appsecret>*********</appsecret> <accesstoken>_VqnWkCBEOch4vUgNB7ssBkYIzcsYgW3azkztMLur-OUqKk95U-Sfaqv9XNvGiToAWee2nybxXCtC5x3ipRoT8WotxmM_vZr-WqmD0_XJ9szMZXsqx57EkndVprc1dBtBAQaAFAMLB</accesstoken> <token>loyung</token> </weixin>
效果:
后面其他接口及返回,就按照同样的方式添加代码。