#region Apache License Version 2.0 /*---------------------------------------------------------------- Copyright 2019 Jeffrey Su & Suzhou Senparc Network Technology Co.,Ltd. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Detail: https://github.com/JeffreySu/WeiXinMPSDK/blob/master/license.md ----------------------------------------------------------------*/ #endregion Apache License Version 2.0 /*---------------------------------------------------------------- Copyright (C) 2019 Senparc 文件名:CommonApi.Menu.Custom.cs 文件功能描述:通用自定义菜单接口(自定义接口) 创建标识:Senparc - 20150211 修改标识:Senparc - 20150303 修改描述:整理接口 修改标识:Senparc - 20150312 修改描述:开放代理请求超时时间 修改标识:Senparc - 201503232 修改描述:修改字符串是否为空判断方式(感谢dusdong) 修改标识:Senparc - 20150703 修改描述:改用accessTokenOrAppId参数 修改标识:IsaacXu - 20151222 修改描述:添加CreateMenu重写方法 修改标识:Senparc - 20180928 修改描述:添加GetCurrentSelfMenuInfo方法 ----------------------------------------------------------------*/ /* API:http://mp.weixin.qq.com/wiki/13/43de8269be54a0a6f64413e4dfa94f39.html */ using System; using System.Collections.Generic; using System.Text; using Senparc.CO2NET.Extensions; using Senparc.CO2NET.HttpUtility; using Senparc.Weixin.Entities; using Senparc.Weixin.Exceptions; using Senparc.Weixin.MP.Entities; using Senparc.Weixin.MP.Entities.Menu; using Senparc.NeuChar; using System.Threading.Tasks; using Senparc.Weixin.CommonAPIs; using Senparc.CO2NET.Helpers; #if NET35 || NET40 || NET45 using System.Web.Script.Serialization; #endif namespace Senparc.Weixin.MP.CommonAPIs { public partial class CommonApi { ///// ///// 特殊符号转义 ///// ///// ///// //private static string ButtonNameEncode(string name) //{ // //直接用UrlEncode不行,显示内容超长 // return name.Replace("&", "%26"); //} /// /// 创建菜单 /// /// AccessToken或AppId。当为AppId时,如果AccessToken错误将自动获取一次。当为null时,获取当前注册的第一个AppId。 /// 菜单内容 /// /// [ApiBind(NeuChar.PlatformType.WeChat_OfficialAccount, "CommonApi.CreateMenu", true)] public static WxJsonResult CreateMenu(string accessTokenOrAppId, ButtonGroup buttonData, int timeOut = Config.TIME_OUT) { return ApiHandlerWapper.TryCommonApi(accessToken => { var urlFormat = Config.ApiMpHost + "/cgi-bin/menu/create?access_token={0}"; ////对特殊符号进行URL转义 //foreach (var button in buttonData.button) //{ // button.name = ButtonNameEncode(button.name);//button.name.UrlEncode(); // if (button is SubButton) // { // var subButtonList = button as SubButton; // foreach (var subButton in subButtonList.sub_button) // { // subButton.name = ButtonNameEncode(button.name);//button.name.UrlEncode(); // } // } //} return CommonJsonSend.Send(accessToken, urlFormat, buttonData, timeOut: timeOut); }, accessTokenOrAppId); } /// /// 创建菜单 /// /// AccessToken或AppId。当为AppId时,如果AccessToken错误将自动获取一次。当为null时,获取当前注册的第一个AppId。 /// 菜单内容 /// /// [ApiBind(NeuChar.PlatformType.WeChat_OfficialAccount, "CommonApi.CreateMenu", true)] public static WxJsonResult CreateMenu(string accessTokenOrAppId, object buttonData, int timeOut = Config.TIME_OUT) { return ApiHandlerWapper.TryCommonApi(accessToken => { var urlFormat = Config.ApiMpHost + "/cgi-bin/menu/create?access_token={0}"; return CommonJsonSend.Send(accessToken, urlFormat, buttonData, timeOut: timeOut); }, accessTokenOrAppId); } #region GetMenu /// /// 获取单击按钮 /// /// /// [Obsolete("配合GetMenuFromJson方法使用")] [ApiBind(NeuChar.PlatformType.WeChat_OfficialAccount, "CommonApi.GetSingleButtonFromJsonObject", true)] private static SingleClickButton GetSingleButtonFromJsonObject(Dictionary objs) { var sb = new SingleClickButton() { key = objs["key"] as string, name = objs["name"] as string, type = objs["type"] as string }; return sb; } /// /// 从JSON字符串获取菜单对象 /// /// /// [Obsolete("此方法通过判断GetMenuResult并结合object类型转换得到结果。结果准确。但更推荐使用GetMenuFromJsonResult方法。")] [ApiBind(NeuChar.PlatformType.WeChat_OfficialAccount, "CommonApi.GetMenuFromJson", true)] public static GetMenuResult GetMenuFromJson(string jsonString) { var finalResult = new GetMenuResult(new ButtonGroup()); try { //@"{""menu"":{""button"":[{""type"":""click"",""name"":""单击测试"",""key"":""OneClick"",""sub_button"":[]},{""name"":""二级菜单"",""sub_button"":[{""type"":""click"",""name"":""返回文本"",""key"":""SubClickRoot_Text"",""sub_button"":[]},{""type"":""click"",""name"":""返回图文"",""key"":""SubClickRoot_News"",""sub_button"":[]},{""type"":""click"",""name"":""返回音乐"",""key"":""SubClickRoot_Music"",""sub_button"":[]}]}]}}" object jsonResult = SerializerHelper.GetObject(jsonString); var fullResult = jsonResult as Dictionary; if (fullResult != null && fullResult.ContainsKey("menu")) { //得到菜单 var menu = fullResult["menu"]; var buttons = (menu as Dictionary)["button"] as object[]; foreach (var rootButton in buttons) { var fullButton = rootButton as Dictionary; if (fullButton.ContainsKey("key") && !string.IsNullOrEmpty(fullButton["key"] as string)) { //按钮,无下级菜单 finalResult.menu.button.Add(GetSingleButtonFromJsonObject(fullButton)); } else { //二级菜单 var subButton = new SubButton(fullButton["name"] as string); finalResult.menu.button.Add(subButton); foreach (var sb in fullButton["sub_button"] as object[]) { subButton.sub_button.Add(GetSingleButtonFromJsonObject(sb as Dictionary)); } } } } else if (fullResult != null && fullResult.ContainsKey("errmsg")) { //菜单不存在 throw new ErrorJsonResultException(fullResult["errmsg"] as string, null, null); } } catch (ErrorJsonResultException ex) { finalResult = null; //如果没有惨淡会返回错误代码:46003:menu no exist } catch (Exception ex) { //其他异常 finalResult = null; } return finalResult; } /// /// 获取当前菜单,如果菜单不存在,将返回null /// /// AccessToken或AppId(推荐使用AppId,需要先注册) /// [ApiBind(NeuChar.PlatformType.WeChat_OfficialAccount, "CommonApi.GetMenu", true)] public static GetMenuResult GetMenu(string accessTokenOrAppId) { return ApiHandlerWapper.TryCommonApi(accessToken => { var url = string.Format(Config.ApiMpHost + "/cgi-bin/menu/get?access_token={0}", accessToken.AsUrlData()); var jsonString = RequestUtility.HttpGet(url, Encoding.UTF8); //var finalResult = GetMenuFromJson(jsonString); GetMenuResult finalResult; try { #if NET35 || NET40 || NET45 JavaScriptSerializer js = new JavaScriptSerializer(); var jsonResult = js.Deserialize(jsonString); #else var jsonResult = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonString); #endif if (jsonResult.menu == null || jsonResult.menu.button.Count == 0) { throw new WeixinMenuException(jsonResult.errmsg); } finalResult = GetMenuFromJsonResult(jsonResult, new ButtonGroup()); } catch (WeixinMenuException) { throw; //finalResult = null; } catch (Exception) { throw; } return finalResult; }, accessTokenOrAppId); } #endregion /// /// 删除菜单 /// /// /// [ApiBind(NeuChar.PlatformType.WeChat_OfficialAccount, "CommonApi.DeleteMenu", true)] public static WxJsonResult DeleteMenu(string accessTokenOrAppId) { return ApiHandlerWapper.TryCommonApi(accessToken => { var url = string.Format(Config.ApiMpHost + "/cgi-bin/menu/delete?access_token={0}", accessToken.AsUrlData()); return CommonJsonSend.Send(null, url, null, CommonJsonSendType.GET); }, accessTokenOrAppId); } #region 同步方法 /// /// 获取自定义菜单配置接口 /// /// /// /// [ApiBind(NeuChar.PlatformType.WeChat_OfficialAccount, "CommonApi.GetCurrentSelfMenuInfo", true)] public static SelfMenuConfigResult GetCurrentSelfMenuInfo(string accessTokenOrAppId, int timeOut = Config.TIME_OUT) { return ApiHandlerWapper.TryCommonApi(accessToken => { var url = string.Format(Config.ApiMpHost + "/cgi-bin/get_current_selfmenu_info?access_token={0}", accessToken.AsUrlData()); return CommonJsonSend.Send(null, url, null, CommonJsonSendType.GET, timeOut: timeOut); }, accessTokenOrAppId); } #endregion #if !NET35 && !NET40 #region 异步方法 /// /// 【异步方法】获取自定义菜单配置接口 /// /// /// /// [ApiBind(NeuChar.PlatformType.WeChat_OfficialAccount, "CommonApi.GetCurrentSelfMenuInfo", true)] public static async Task GetCurrentSelfMenuInfoAsync(string accessTokenOrAppId, int timeOut = Config.TIME_OUT) { return await ApiHandlerWapper.TryCommonApiAsync(async accessToken => { var url = string.Format(Config.ApiMpHost + "/cgi-bin/get_current_selfmenu_info?access_token={0}", accessToken.AsUrlData()); return await CommonJsonSend.SendAsync(null, url, null, CommonJsonSendType.GET, timeOut: timeOut); }, accessTokenOrAppId); } #endregion #endif } }