微信公众号自定义菜单
参见微信公众平台接口文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1445241432
保存到微信代码
[HttpPost("SaveToWx")]
[AbpAllowAnonymous]//无需登录就可以调用 [AbpAllowAnonymous] public async Task<ApiResult> SaveToWx() { ApiResult apiResult = new ApiResult(); try { string AppId = await SettingManager.GetSettingValueAsync(AppSettings.WeChatManagement.AppId); string AppSecret = await SettingManager.GetSettingValueAsync(AppSettings.WeChatManagement.AppSecret); // string Token = await SettingManager.GetSettingValueAsync(AppSettings.WeChatManagement.Token); if (string.IsNullOrEmpty(AppId) || string.IsNullOrEmpty(AppSecret)) { apiResult.ReturnCode = ReturnCodes.不合法的APPID; apiResult.Message = "服务号配置存在问题"; } MenuInfo menuInfo = new MenuInfo(); menuInfo.Button = new List<MenuButtonBase>(); List<MenuSettingInfo> initMenus = GetInitMenus(); List < MenuButtonBase> menu = new List<MenuButtonBase>(); foreach (MenuSettingInfo current in initMenus) { string url = _httpContextAccessor.HttpContext.Request.Host.ToString(); switch ((BindType)current.Bind) { case BindType.None://不绑定 case BindType.Member: current.Url = url; break; case BindType.Card: current.Url = "http://" + url + "/Wap/Card"; break; case BindType.Store: current.Url = "http://" + url + "/Wap/Store"; break; case BindType.Message: current.Url = "http://" + url + "/Wap/Message"; break; case BindType.Key: current.Url = current.ReplyId.ToString(); break; default: break; } if (current.Chilren == null || current.Chilren.Count == 0) { MenuButtonBase menuButtonBase = this.BuildMenu(current); menuButtonBase.Name = current.Name; menuButtonBase.Key = current.Id.ToString(); menuButtonBase.AppId = AppId; menuButtonBase.Type = MenuButtonTypes.view; menu.Add(menuButtonBase); } else { //一级菜单 MenuButtonBase buttonBase = new MenuButtonBase(); buttonBase.MediaId = "0"; buttonBase.SubButton = new List<MenuButtonBase>(); buttonBase.NewsInfo = new List<NewsInfo>(); buttonBase.Value= current.Id.ToString(); buttonBase.Name = current.Name; buttonBase.Key = current.Id.ToString(); buttonBase.Url = current.Url; buttonBase.Pagepath = current.Url; buttonBase.AppId = AppId; if (current.Type == "view") { buttonBase.Type = MenuButtonTypes.view; } else if (current.Type == "click") { buttonBase.Type = MenuButtonTypes.click; } //二级菜单 foreach (MenuSettingInfo current2 in current.Chilren) { MenuButtonBase menuButtonBase = this.BuildMenu(current2); menuButtonBase.AppId = AppId; buttonBase.SubButton.Add(menuButtonBase);//子菜单(二级菜单数组,个数应为1~5个) } menu.Add(buttonBase); } } menuInfo.Button.AddRange(menu); string json = JsonConvert.SerializeObject(menuInfo); MenuApi menuApi = new MenuApi(); MenuInfo newmenuInfo= JsonConvert.DeserializeObject<MenuInfo>(json, new MenuButtonsCustomConverter()); ApiResult text = menuApi.Create(newmenuInfo); // var url = GetAccessApiUrl("create", "menu"); // ApiResult text= Post<ApiResult>(url, newmenuInfo); // ApiResult text = menuApi.Create(menuInfo); // ApiResult text = menuApi.CreateByJson(json); if (text.IsSuccess()) { apiResult.ReturnCode = ReturnCodes.请求成功; apiResult.Message = "成功的把自定义菜单保存到了微信"; } else { apiResult.ReturnCode = ReturnCodes.解析JSON_XML内容错误; apiResult.Message = text.DetailResult; } } catch (Exception ex) { throw ex; //apiResult.Message = string.Concat(new string[] // { // ex.Message, // "---", // Token, // "---", // AppId, // "---", // AppSecret // }); } return apiResult; } private List<MenuSettingInfo> GetInitMenus() { var MenuList = new List<MenuSettingInfo>(); var query = _menu_Repository.GetAllList(m => m.ParentMenuId == 0); foreach (Media_MenuSetting_Info current in query) { var topMenus = new MenuSettingInfo(); topMenus = ConvertMenuSettingInfo(current); var List = new List<MenuSettingInfo>(); var Chilrenlist = _menu_Repository.GetAllList(m => m.ParentMenuId == current.Id); foreach (Media_MenuSetting_Info chilren in Chilrenlist) { var Chilren = new MenuSettingInfo(); Chilren = ConvertMenuSettingInfo(chilren); List.Add(Chilren); } topMenus.Chilren = List; if (topMenus.Chilren == null) { topMenus.Chilren = new List<MenuSettingInfo>(); } MenuList.Add(topMenus); } return MenuList; } private MenuButtonBase BuildMenu(MenuSettingInfo menu) { MenuButtonTypes menuButtonTypes = 0; if (menu.Type == "view") { menuButtonTypes = MenuButtonTypes.view; } else if (menu.Type == "click") { menuButtonTypes = MenuButtonTypes.click; } switch ((BindType)menu.Bind) { case BindType.None: case BindType.Key: return new MenuButtonBase { Name = menu.Name, MediaId = "0", Type = menuButtonTypes, SubButton = new List<MenuButtonBase>(), NewsInfo=new List<NewsInfo> (), Key = menu.Id.ToString(), Value = menu.Id.ToString(), Url = menu.Url, Pagepath = menu.Url }; case BindType.Member: return new MenuButtonBase { Name = menu.Name, MediaId = "", Type = menuButtonTypes, SubButton = new List<MenuButtonBase>(), NewsInfo = new List<NewsInfo>(), Key = menu.Id.ToString(), Value = menu.Id.ToString(), Url = menu.Url, Pagepath = menu.Url }; case BindType.Card: return new MenuButtonBase { Name = menu.Name, MediaId = "", Type = menuButtonTypes, SubButton = new List<MenuButtonBase>(), NewsInfo = new List<NewsInfo>(), Key = menu.Id.ToString(), Value = menu.Id.ToString(), Url = menu.Url, Pagepath = menu.Url }; case BindType.Store: return new MenuButtonBase { Name = menu.Name, MediaId = "", Type = menuButtonTypes, SubButton = new List<MenuButtonBase>(), NewsInfo = new List<NewsInfo>(), Key = menu.Id.ToString(), Value = menu.Id.ToString(), Url = menu.Url, Pagepath = menu.Url }; case BindType.Message: return new MenuButtonBase { Name = menu.Name, MediaId = "", Type = menuButtonTypes, SubButton=new List<MenuButtonBase> (), NewsInfo = new List<NewsInfo>(), Key = menu.Id.ToString(), Value = menu.Id.ToString(), Url = menu.Url, Pagepath = menu.Url }; } return new MenuButtonBase { Name = menu.Name, Key = "None", MediaId = "0", Type = menuButtonTypes, SubButton = new List<MenuButtonBase>(), NewsInfo = new List<NewsInfo>(), Value = menu.Id.ToString(), Url = menu.Url, Pagepath = menu.Url }; }
保存到微信时需要注意的问题
一、测试自定义菜单接口时中文菜单名显示为null
设置的中文菜单名,中文未经过编码和解码过程,设置的中文菜单名在最后的微信服务器返回的json格式数据中显示为null。
解决办法:将中文先用unecode方法编码,最后再将菜单数组用undecode解码,再传给微信服务器。方法最上面加上header("content-type=text/html;charset=utf-8"),编码方式必须是utf-8,才能在微信公众平台在线测试接口。
二、自定义菜单中的菜单类型type="click"(或"view")必须小写
大写CLICK和VIEW会出错,错误提示:must use utf-8 charse。
三、自定义菜单类型type为view时,设置的url必须有值,设置为"",会出错
url设置为"",或url不正确,修改后的菜单名都不会得到更新。url的格式必须为"http://" + url + "/。。。/。。。"的格式
四、自定义菜单时,更改菜单名之后微信公众号上得不到及时更新
菜单未得到更新的情况:一是,写的菜单数组代码语法错误,代码有问题;可先测试一下接口,测试代码见我写的微信公众号开发之自定义菜单
二是,有的时候我们在多个环境中去调用access_token,会出现改变了文件却显示的是前一个文件中的菜单的情况;
五、
下面引入一位博友写的总结:
如果有第二地方也请求同一个token的话,那么第一个token会在5分钟之内过期。这也就说明了,为什么在我搭建好第二个环境的时候,老环境就出现了invalid credential, access_token is invalid or not latest hint 这种问题。因为这两个环境用的是同 一个AppID和AppSecret来取得的access_token,而这个access_token的取得并不是在服务器启动的时候,而且是在需要调用接口的画面初期化的时候去取得的。
我的理解是在多个环境中去调用access_token,当在另一个环境中调用access_token时,在一定时间内前一个环境中获取的access_token还未过期,所以在这段时间中显示的是前一个环境中设置的菜单。
access_token的获 取和服务器什么时候启动没有直接的关系,有直接关系的是初次调用接口是什么时候。
注:修改回复消息之后可以在公众号立即看到更新,但是修改菜单之后,公众号不会马上更新菜单,修改菜单代码之后,最好是取消公众号关注,然后再刷新测试号管理(以测试号为例)界面,再扫描二维码重新关注。