MVC下c#对接微信公众平台开发者模式
在ashx文件中进行HttpContext的处理:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using Biz.WX; namespace weixinplat { /// <summary> /// WeiXin 的摘要说明 /// </summary> public class WeiXin : IHttpHandler { public void ProcessRequest(HttpContext context) { try { string postString = string.Empty;//初始化空字符串来转抓到的request请求的bit流 context.Response.ContentType = "text/plain"; if (context.Request.HttpMethod.ToLower() == "post")//如果从requset的httpmethod方法是post就进行图文等处理 { } else//否则就是get方式,进行校验认证 { AccessToken.Auth(); } } catch (Exception ex) { throw ex; } } public bool IsReusable { get { return false; } } } }
在专门处理数据的类库Biz中校验:
其中AccessToken类用来处理token:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web; using System.Configuration; namespace Biz.WeiXin { public class AccessToken { /// <summary> /// 这个函数是初始化配置服务器地址 /// </summary> public static void Auth() { string echoStr = HttpContext.Current.Request.QueryString["echoStr"]; if (CheckSignature()) //校验签名是否正确 { if (!string.IsNullOrEmpty(echoStr)) { HttpContext.Current.Response.Write(echoStr); HttpContext.Current.Response.End(); } } } /// <summary> /// 校验微信公众平台签名函数 /// </summary> /// <returns></returns> public static bool CheckSignature() { string signature = HttpContext.Current.Request.QueryString["signature"]; string timestamp = HttpContext.Current.Request.QueryString["timestamp"]; string nonce = HttpContext.Current.Request.QueryString["nonce"]; string token = ConfigurationManager.AppSettings["weixin_token"]; string[] tmpArr = { token , timestamp, nonce }; Array.Sort(tmpArr); string tmpStr = string.Join("", tmpArr); tmpStr = WX.Sha1_Hash(tmpStr); if (tmpStr == signature) { return true; } else { return false; } } } }
WX类用来处理哈希算法:
using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; namespace Biz.WX { public class WX { public static string Sha1_Hash(string str_sha1_in) { SHA1 sha1 = new SHA1CryptoServiceProvider(); byte[] bytes_sha1_in = UTF8Encoding.Default.GetBytes(str_sha1_in); byte[] bytes_sha1_out = sha1.ComputeHash(bytes_sha1_in); string str_sha1_out = BitConverter.ToString(bytes_sha1_out); str_sha1_out = str_sha1_out.Replace("-", "").ToLower(); return str_sha1_out; } } }
- 将菜单发布到微信公众平台
在treegrid空间的toolbar工具栏按钮中通过ajax来连接到后台方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{ id: 'btnRefresh', text: '发布到微信公众平台', iconCls: 'icon-redo', handler: function () { $.ajax({ url: "/WxMenu/MenuToWeiXin", data: {}, dataType: "json", type: "POST", traditional: true, beforeSend: function () { showProcess(true, "系统提示", "正发布到微信公众平台......"); }, error: function () { }, success: function (result) { showMsg("系统提示", result.Message, false); }, complete: function () { showProcess(false); } }); }
连接到后台的MenuToWeiXin方法返回json对象:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public JsonResult MenuToWeiXin() { try { MenuManager.CreateMenu(); return Json(new { Success = true, Message = "请求成功" }); } catch (Exception ex) { return Json(new { Success = false,Message = ex.Message }); } }
其中 MenuManager.CreateMenu()方法就是去将微信菜单对接到微信API上面。如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public static void CreateMenu() { NHibernateHelper nhlper = new NHibernateHelper(); ISession session = nhlper.GetSession(); IEnumerable<WeiXinMenu> kinds = session.Query<WeiXinMenu>(); if (kinds.Count() <= 1) { throw new Exception("请先配置菜单"); } string menu = ""; menu += "{\"button\":["; kinds.Where(c => c.ParentId == "10000").Foreach(c => { menu += "{"; menu += "\"name\":\"{0}\",".FormartWith(c.MenuName); menu += "\"sub_button\":["; kinds.Where(m=>m.ParentId==c.MenuId).Foreach(m=> { menu += "{"; menu += "\"type\":\"{0}\",".FormartWith(m.MenuType); menu += "\"name\":\"{0}\",".FormartWith(m.MenuName); if (m.MenuType == "click") { menu += "\"key\":\"{0}\"".FormartWith(m.MenuKey); } else { menu += "\"url\":\"{0}\"".FormartWith(m.MenuUrl); } menu += "},"; }); menu = menu.Remove(menu.Length - 1, 1); menu += "]"; menu += "},"; }); menu = menu.Remove(menu.Length - 1, 1); menu += "]"; menu += "}"; string url = url_menu_create + AccessToken.Weixin_ACCESS_TOKEN; string responsestring = HttpUtils.GetHttprequest(url); JObject result = JsonConvert.DeserializeObject(responsestring) as JObject; string errcode = result["errcode"].ToString(); string errmsg = result["errmsg"].ToString(); if (errcode != "0") { throw new Exception(Weixin_Errorcode.GetErrormsg(int.Parse(errcode))); } }
其中url_menu_create是微信给的API接口 private static string url_menu_create = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=";
然后将查询到的菜单组装成微信后台需要的json格式。然后调用接口,用post方式的httprequest去访问。可以根据返回的错误代码来知道具体问题是在哪里。
tips:调用微信的很多API接口都需要微信的access_token,所以可以单独写个类来获取:其中AccessToken.Weixin_ACCESS_TOKEN就是获取到微信的access_token:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web; using System.Configuration; using System.Security.Cryptography; using Business.Extsion; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace Business.weixin { public class AccessToken { public static string AppID = ConfigurationManager.AppSettings["weixin_AppID"]; public static string AppSecret = ConfigurationManager.AppSettings["weixin_AppSecret"]; public static string mAccessToken; public static DateTime GettokenTime; public static int Expires_Period = 7200;//token有效时间,默认2小时 /// <summary> /// 正经对接微信公众平台函数。获取echoStr,来返回。 /// </summary> public static void Auth() { string echoStr = HttpContext.Current.Request.QueryString["echoStr"]; if (CheckSignature()) { if (!string.IsNullOrEmpty(echoStr)) { HttpContext.Current.Response.Write("echoStr"); HttpContext.Current.Response.End(); } } } /// <summary> /// 校验函数,微信后台返回的signature和token+timestamp+nonce 对比 /// </summary> /// <returns></returns> public static bool CheckSignature() { string signature = HttpContext.Current.Request.QueryString["signature"]; string timestamp = HttpContext.Current.Request.QueryString["timestamp"]; string nonce = HttpContext.Current.Request.QueryString["nonce"]; string token = ConfigurationManager.AppSettings["weixintoken"]; string[] tmpArr = { token, timestamp, nonce }; Array.Sort(tmpArr); string tmpStr = string.Join("", tmpArr); tmpStr = Sha1_Hash(tmpStr);//通过hash算法得到一个字符串,跟signature对比 if (tmpStr == signature) { return true; } else { return false; } } public static string Sha1_Hash(string intputstr) { SHA1 sha1 =new SHA1CryptoServiceProvider(); byte[] byte_in = UTF8Encoding.Default.GetBytes(intputstr); byte[] byte_out = sha1.ComputeHash(byte_in); string outputstr = BitConverter.ToString(byte_out); outputstr = outputstr.Replace("-","").ToLower(); return outputstr; } public static string Weixin_ACCESS_TOKEN { get{ //如果为空或者过期。则重新获取 if (string.IsNullOrEmpty(mAccessToken) || HasExpired()) { //获取accesstoken函数 mAccessToken = GetAccessToken(AppID, AppSecret); } return mAccessToken; } } /// <summary> /// 判断是不是token过期 /// </summary> /// <returns></returns> public static bool HasExpired() { if (GettokenTime != null) { if (DateTime.Now > GettokenTime.AddSeconds(7200).AddSeconds(-60)) { return true; } } return false; } /// <summary> /// 获取accesstoken函数 /// </summary> /// <param name="AppID"></param> /// <param name="AppSecret"></param> /// <returns></returns> public static string GetAccessToken(string AppID,string AppSecret) { string url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}"; url = string.Format(url, AppID, AppSecret); string responsestring = HttpUtils.GetHttprequest(url); JObject result = JsonConvert.DeserializeObject(responsestring) as JObject; if (result["access_token"] != null) { GettokenTime = DateTime.Now; if (result["expires_in"] != null) { Expires_Period = int.Parse(result["expires_in"].ToString()); } return result["access_token"].ToString(); } else { GettokenTime = DateTime.MinValue; } return null; } } }
在访问这种api接口的时候都需要主动去访问,所以可以把httprequest请求单独写在一个类HttpUtils中:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; namespace Business.Extsion { public class HttpUtils { /// <summary> /// 发起一个post类型的http请求 /// </summary> /// <param name="url"></param> /// <param name="data"></param> /// <returns></returns> public static string SendHttprequest(string url,string data) { return SendPostHttprequest( url, "application/x-www-form-urlencoded", data); } /// <summary> /// 发起一个get模式的http请求 /// </summary> /// <returns></returns> public static string GetHttprequest(string url) { return SendGetHttprequest(url,"application/x-www-form-urlencoded"); } public static string SendPostHttprequest(string url,string contentType, string requestData) { WebRequest request = (WebRequest)HttpWebRequest.Create(url); request.Method = "POST"; request.ContentType = contentType; byte[] postbytes = Encoding.UTF8.GetBytes(requestData); request.ContentLength = postbytes.Length; using (Stream outstream = request.GetRequestStream()) { outstream.Write(postbytes, 0, postbytes.Length); } string result = string.Empty; using (WebResponse response = request.GetResponse()) { if (response !=null) { using (Stream getstream = response.GetResponseStream()) { using (StreamReader reader = new StreamReader(getstream,Encoding.UTF8)) { result = reader.ReadToEnd(); } } } } return result; } public static string SendGetHttprequest(string url ,string contenType) { WebRequest request = (WebRequest)HttpWebRequest.Create(url); request.Method = "GET"; request.ContentType = contenType; string result = string.Empty; using (WebResponse response = request.GetResponse()) { if (response != null) { using (Stream resstream = response.GetResponseStream()) { using (StreamReader reader = new StreamReader(resstream,Encoding.UTF8) ) { result = reader.ReadToEnd(); } } } } return result; } } }
- 编辑新增菜单
通过控制器返回视图:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public ActionResult MenuEdit(int id) { NHibernateHelper nhlper = new NHibernateHelper(); ISession session = nhlper.GetSession(); WeiXinMenu model = session.Get<WeiXinMenu>(id); if (model == null) { model = new WeiXinMenu(); model.IsEnable = "1"; model.CreateTime = DateTime.Now; } return View(model); }
控制器动作跟着返回查询到的model在视图上显示:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@model Domain.OrmLib.Entity.WeiXinMenu @{ ViewBag.Title = "MenuEdit"; Layout = "~/Views/Shared/_Form.cshtml"; } @section header { } @section body{ @using (Html.BeginForm("MenuSaveOrUpdate", "WeiXin", FormMethod.Post, new { id = "dataForm" })) { <table class="datalistDaily" cellpadding="5" style="font-size:12px;margin-top:10px;margin-left:10px;"> <tr> <td style="text-align:right;">菜单编码:</td> <td> <input class="easyui-textbox" id="MenuId" name="MenuId" style="width:300px;" data-options="required:true" value="@Model.MenuId" /> </td> </tr> <tr> <td style="text-align:right;">菜单名字:</td> <td> <input class="easyui-textbox" id="MenuName" name="MenuName" style="width:300px;" data-options="required:true" value="@Model.MenuName" /> </td> </tr> <tr> <td style="text-align:right;">动作类型:</td> <td> <select id="MenuType" name="MenuType" class="easyui-combobox" style="width:300px;"> <option selected="selected" value="view">跳转URL</option> <option value="click">点击推事件</option> <option value="scancode_push">扫码推事件</option> <option value="scancode_waitmsg">扫码推事件且弹出(消息接收中)提示框</option> <option value="pic_sysphoto">弹出系统拍照发图</option> <option value="pic_photo_or_album">弹出拍照或者相册发图</option> <option value="pic_weixin">弹出微信相册发图器</option> <option value="location_select">弹出地理位置选择器</option> <option value="media_id">下发消息(除文本消息)</option> <option value="view_limited">跳转图文消息URL</option> </select> </td> </tr> <tr> <td style="text-align:right;">菜单Url:</td> <td> <input class="easyui-textbox" id="MenuUrl" name="MenuUrl" style="width:300px;" value="@Model.MenuUrl" /> </td> </tr> <tr> <td style="text-align:right;">菜单Key:</td> <td> <input class="easyui-textbox" id="MenuKey" name="MenuKey" style="width:300px;" value="@Model.MenuKey" /> </td> </tr> <tr> <td style="text-align:right;">菜单排序:</td> <td> <input class="easyui-textbox" id="OrderBy" name="OrderBy" data-options="required:true" style="width:300px;" value="@Model.OrderBy" /> </td> </tr> <tr> <td style="text-align:right;">菜单状态:</td> <td> <select id="IsEnable" name="IsEnable" class="easyui-combobox" style="width:300px;"> <option selected="selected" value="1">启用菜单</option> <option value="0">禁用菜单</option> </select> </td> </tr> <tr> <td style="text-align:right;">上级菜单:</td> <td> <input class="easyui-combotree" name="ParentId" id="ParentId" style="width:300px;" /> </td> </tr> <tr> <td colspan="2" align="center"></td> </tr> <tr> <td colspan="2" align="center"> @Html.HiddenFor(model => model.Id) @Html.HiddenFor(model => model.UserNum) @Html.HiddenFor(model => model.UserName) @Html.HiddenFor(model => model.CreateTime) <a icon="icon-ok" class="easyui-linkbutton" onclick="save()">存盘</a> </td> </tr> </table> } } @section scripts { <script src="~/Content/oa/scripts/xlayout.js"></script> <script type="text/javascript"> $(function () { $('#ParentId').combotree({ url: "/WxMenu/MenuTree", required: true, multiple: false, lines: true, queryParams: { ids: '@Model.ParentId' }, onLoadSuccess: function () { $('#ParentId').combotree('setValue', '@Model.ParentId'); } }); $('#MenuType').combobox('setValue', '@Model.MenuType'); $('#IsEnable').combobox('setValue', '@Model.IsEnable'); }) function save() { alert(2212); var dataForm = $("#dataForm").form('validate'); if (!dataForm) return; showProcess(true, "系统提示", "正在存盘中......"); $.ajax({ url: "/WxMenu/MenuSaveOrUpdate", data: $("#dataForm").serialize(), dataType: "json", type: "POST", traditional: true, success: function (result) { showProcess(false); if (result.Success) { var sys_main = $(self.top.document).find("#sys_main").get(0).contentWindow; sys_main.radWindowCallBackFn(); var url = document.URL; url = url.replace("http://" + window.location.host, ""); url = url.replace(/\//g, "_"); sys_main.CloseWindow(url); } else { showMsg("系统提示", result.Message, false); } } }); return false; } </script> }
通过ajax在后台进行saveorupdate