C#微信公众号开发-MVC模式公共类封装
第一部分:基础配置
第一步:注册微信公众账号
如果开发测试阶段可以打开测试链接地址,注册测试公众号。测试账号除了不能与正式账号通信外其他什么高级接口的都可以实现。
测试号管理地址: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") != -)
{
var ErrorMessage = JsonConvert.DeserializeObject<wx_apperror>(GetResult);
if (ErrorMessage.errcode == "")
{
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") != -)
{
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>
效果:

后面其他接口及返回,就按照同样的方式添加代码。
C#微信公众号开发-MVC模式公共类封装的更多相关文章
- 线程安全使用(四) [.NET] 简单接入微信公众号开发:实现自动回复 [C#]C#中字符串的操作 自行实现比dotcore/dotnet更方便更高性能的对象二进制序列化 自已动手做高性能消息队列 自行实现高性能MVC WebAPI 面试题随笔 字符串反转
线程安全使用(四) 这是时隔多年第四篇,主要是因为身在东软受内网限制,好多文章就只好发到东软内部网站,懒的发到外面,现在一点点把在东软写的文章给转移出来. 这里主要讲解下CancellationT ...
- VopSdk一个高逼格微信公众号开发SDK:自动化生产(装逼模式开启)
VopSdk一个高逼格微信公众号开发SDK(源码下载) VopSdk一个高逼格微信公众号开发SDK:自动化生产(装逼模式开启) 针对第一版,我们搞了第二版本,老规矩先定个目标. 一 我们的目标 a.移 ...
- C#微信公众号开发系列教程四(接收普通消息)
微信公众号开发系列教程一(调试环境部署) 微信公众号开发系列教程一(调试环境部署续:vs远程调试) C#微信公众号开发系列教程二(新手接入指南) C#微信公众号开发系列教程三(消息体签名及加解密) C ...
- VopSdk一个高逼格微信公众号开发SDK(源码下载)
看之前回复很多说明大家很有热情&文章被误删掉了,不想让有的朋友错失这个高逼格的东西,现在重新发布,这次就直接放出源码,文章最末下载地址. 看之前回复很多说明大家很有热情&文章被误删掉了 ...
- 微信公众号开发C#系列-2、微信公众平台接入指南
概述 微信公众平台消息接口的工作原理大概可以这样理解:从用户端到公众号端一个流程是这样的,用户发送消息到微信服务器,微信服务器将接收到的消息post到用户接入时填写的url中,在url处理程序中,首先 ...
- 微信公众号开发系列-13、基于RDIFramework.NET框架整合微信开发应用效果展示
1.前言 通过前面一系列文章的学习,我们对微信公众号开发已经有了一个比较深入和全面的了解. 微信公众号开发为企业解决那些问题呢? 我们经常看到微信公众号定制开发.微信公众平台定制开发,都不知道这些能给 ...
- 微信公众号开发--.net core接入
.net进行微信公众号开发的例子好像比较少,这里做个笔记 首先,我们需要让微信能访问到我们的项目,所以要么需要有一个可以部署项目的连接到公网下的服务器,要么可以通过端口转发将请求转发到我们的项目,总之 ...
- C#微信公众号开发系列教程三(消息体签名及加解密)
http://www.cnblogs.com/zskbll/p/4139039.html C#微信公众号开发系列教程一(调试环境部署) C#微信公众号开发系列教程一(调试环境部署续:vs远程调试) C ...
- C#微信公众号开发系列教程二(新手接入指南)
http://www.cnblogs.com/zskbll/p/4093954.html 此系列前面已经更新了两篇博文了,都是微信开发的前期准备工作,现在切入正题,本篇讲解新手接入的步骤与方法,大神可 ...
随机推荐
- [Android] Web Console: Uncaught TypeError: Object [object Object] has no method 'xxx'
我们开发的产品,有一部分功能,需要在WebView中打开web页面,然后在web页面中通过js方法回调部分native的功能. 对于web回调native的开发方式,如果不了解的话,可以参考我以前的一 ...
- JS转换时间戳为“刚刚”、“1分钟前”、“2小时前”“1天前”等格式
var minute = 1000 * 60; var hour = minute *60; var day = hour *24; var week = day * 7; var month = d ...
- centos7.0 64位系统安装 nginx
1 下载nginx 从nginx官网 http://nginx.org/ 下载新的稳定版本nginx 并上传到linux服务器 2 安装nginx 所需要的扩展 yum -y install pcre ...
- 用MySQL实现分页查询
MySQL中实现分页查询语句: //定义分页需要的变量 int pageNow=2;//当前页 int pageSize=3;//指定每页显示3条记录 int pageCount=1;//该值是计算出 ...
- python打印目录下的文件名
打印当前目录所有文件名 import fnmatch, os def allFiles(root, patterns = '*', single_level = False, yield_folder ...
- cocos2d-x 的两大基类
cocos2d-x 有两个重要的基类,一个管理引用计数的 Ref,别一个则定义许多基本属性的 Node. 在 cocos2d-x 中的基本概念 说到 create 函数的时候提到 cocos2d-x ...
- Linux I2C总线设备驱动模型分析(ov7740)
1. 框架1.1 硬件协议简介1.2 驱动框架1.3 bus-drv-dev模型及写程序a. 设备的4种构建方法a.1 定义一个i2c_board_info, 里面有:名字, 设备地址 然后i2c_r ...
- 关于async
以前做项目很少理会async这个属性 今天做项目的时候 由于原来是点击查看按钮进行查看 现在要把需求改成默认进入页面就直接显示内容 我在js加载的时候就需要调用一次查看的点击事件 我在整个js初始加载 ...
- zookeeper初识之原理
ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务,它包含一个简单的原语集,分布式应用程序可以基于它实现同步服务,配置维护和命名服务等. Zookeeper是hadoop的一个子项目 ...
- HTMl5-canvas 入门级复习
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...