CommonApi.Menu.Custom.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. #region Apache License Version 2.0
  2. /*----------------------------------------------------------------
  3. Copyright 2019 Jeffrey Su & Suzhou Senparc Network Technology Co.,Ltd.
  4. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
  5. except in compliance with the License. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software distributed under the
  8. License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
  9. either express or implied. See the License for the specific language governing permissions
  10. and limitations under the License.
  11. Detail: https://github.com/JeffreySu/WeiXinMPSDK/blob/master/license.md
  12. ----------------------------------------------------------------*/
  13. #endregion Apache License Version 2.0
  14. /*----------------------------------------------------------------
  15. Copyright (C) 2019 Senparc
  16. 文件名:CommonApi.Menu.Custom.cs
  17. 文件功能描述:通用自定义菜单接口(自定义接口)
  18. 创建标识:Senparc - 20150211
  19. 修改标识:Senparc - 20150303
  20. 修改描述:整理接口
  21. 修改标识:Senparc - 20150312
  22. 修改描述:开放代理请求超时时间
  23. 修改标识:Senparc - 201503232
  24. 修改描述:修改字符串是否为空判断方式(感谢dusdong)
  25. 修改标识:Senparc - 20150703
  26. 修改描述:改用accessTokenOrAppId参数
  27. 修改标识:IsaacXu - 20151222
  28. 修改描述:添加CreateMenu重写方法
  29. 修改标识:Senparc - 20180928
  30. 修改描述:添加GetCurrentSelfMenuInfo方法
  31. ----------------------------------------------------------------*/
  32. /*
  33. API:http://mp.weixin.qq.com/wiki/13/43de8269be54a0a6f64413e4dfa94f39.html
  34. */
  35. using System;
  36. using System.Collections.Generic;
  37. using System.Text;
  38. using Senparc.CO2NET.Extensions;
  39. using Senparc.CO2NET.HttpUtility;
  40. using Senparc.Weixin.Entities;
  41. using Senparc.Weixin.Exceptions;
  42. using Senparc.Weixin.MP.Entities;
  43. using Senparc.Weixin.MP.Entities.Menu;
  44. using Senparc.NeuChar;
  45. using System.Threading.Tasks;
  46. using Senparc.Weixin.CommonAPIs;
  47. using Senparc.CO2NET.Helpers;
  48. #if NET35 || NET40 || NET45
  49. using System.Web.Script.Serialization;
  50. #endif
  51. namespace Senparc.Weixin.MP.CommonAPIs
  52. {
  53. public partial class CommonApi
  54. {
  55. ///// <summary>
  56. ///// 特殊符号转义
  57. ///// </summary>
  58. ///// <param name="name"></param>
  59. ///// <returns></returns>
  60. //private static string ButtonNameEncode(string name)
  61. //{
  62. // //直接用UrlEncode不行,显示内容超长
  63. // return name.Replace("&", "%26");
  64. //}
  65. /// <summary>
  66. /// 创建菜单
  67. /// </summary>
  68. /// <param name="accessTokenOrAppId">AccessToken或AppId。当为AppId时,如果AccessToken错误将自动获取一次。当为null时,获取当前注册的第一个AppId。</param>
  69. /// <param name="buttonData">菜单内容</param>
  70. /// <param name="timeOut"></param>
  71. /// <returns></returns>
  72. [ApiBind(NeuChar.PlatformType.WeChat_OfficialAccount, "CommonApi.CreateMenu", true)]
  73. public static WxJsonResult CreateMenu(string accessTokenOrAppId, ButtonGroup buttonData, int timeOut = Config.TIME_OUT)
  74. {
  75. return ApiHandlerWapper.TryCommonApi(accessToken =>
  76. {
  77. var urlFormat = Config.ApiMpHost + "/cgi-bin/menu/create?access_token={0}";
  78. ////对特殊符号进行URL转义
  79. //foreach (var button in buttonData.button)
  80. //{
  81. // button.name = ButtonNameEncode(button.name);//button.name.UrlEncode();
  82. // if (button is SubButton)
  83. // {
  84. // var subButtonList = button as SubButton;
  85. // foreach (var subButton in subButtonList.sub_button)
  86. // {
  87. // subButton.name = ButtonNameEncode(button.name);//button.name.UrlEncode();
  88. // }
  89. // }
  90. //}
  91. return CommonJsonSend.Send<WxJsonResult>(accessToken, urlFormat, buttonData, timeOut: timeOut);
  92. }, accessTokenOrAppId);
  93. }
  94. /// <summary>
  95. /// 创建菜单
  96. /// </summary>
  97. /// <param name="accessTokenOrAppId">AccessToken或AppId。当为AppId时,如果AccessToken错误将自动获取一次。当为null时,获取当前注册的第一个AppId。</param>
  98. /// <param name="buttonData">菜单内容</param>
  99. /// <param name="timeOut"></param>
  100. /// <returns></returns>
  101. [ApiBind(NeuChar.PlatformType.WeChat_OfficialAccount, "CommonApi.CreateMenu", true)]
  102. public static WxJsonResult CreateMenu(string accessTokenOrAppId, object buttonData, int timeOut = Config.TIME_OUT)
  103. {
  104. return ApiHandlerWapper.TryCommonApi(accessToken =>
  105. {
  106. var urlFormat = Config.ApiMpHost + "/cgi-bin/menu/create?access_token={0}";
  107. return CommonJsonSend.Send<WxJsonResult>(accessToken, urlFormat, buttonData, timeOut: timeOut);
  108. }, accessTokenOrAppId);
  109. }
  110. #region GetMenu
  111. /// <summary>
  112. /// 获取单击按钮
  113. /// </summary>
  114. /// <param name="objs"></param>
  115. /// <returns></returns>
  116. [Obsolete("配合GetMenuFromJson方法使用")]
  117. [ApiBind(NeuChar.PlatformType.WeChat_OfficialAccount, "CommonApi.GetSingleButtonFromJsonObject", true)]
  118. private static SingleClickButton GetSingleButtonFromJsonObject(Dictionary<string, object> objs)
  119. {
  120. var sb = new SingleClickButton()
  121. {
  122. key = objs["key"] as string,
  123. name = objs["name"] as string,
  124. type = objs["type"] as string
  125. };
  126. return sb;
  127. }
  128. /// <summary>
  129. /// 从JSON字符串获取菜单对象
  130. /// </summary>
  131. /// <param name="jsonString"></param>
  132. /// <returns></returns>
  133. [Obsolete("此方法通过判断GetMenuResult并结合object类型转换得到结果。结果准确。但更推荐使用GetMenuFromJsonResult方法。")]
  134. [ApiBind(NeuChar.PlatformType.WeChat_OfficialAccount, "CommonApi.GetMenuFromJson", true)]
  135. public static GetMenuResult GetMenuFromJson(string jsonString)
  136. {
  137. var finalResult = new GetMenuResult(new ButtonGroup());
  138. try
  139. {
  140. //@"{""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"":[]}]}]}}"
  141. object jsonResult = SerializerHelper.GetObject<object>(jsonString);
  142. var fullResult = jsonResult as Dictionary<string, object>;
  143. if (fullResult != null && fullResult.ContainsKey("menu"))
  144. {
  145. //得到菜单
  146. var menu = fullResult["menu"];
  147. var buttons = (menu as Dictionary<string, object>)["button"] as object[];
  148. foreach (var rootButton in buttons)
  149. {
  150. var fullButton = rootButton as Dictionary<string, object>;
  151. if (fullButton.ContainsKey("key") && !string.IsNullOrEmpty(fullButton["key"] as string))
  152. {
  153. //按钮,无下级菜单
  154. finalResult.menu.button.Add(GetSingleButtonFromJsonObject(fullButton));
  155. }
  156. else
  157. {
  158. //二级菜单
  159. var subButton = new SubButton(fullButton["name"] as string);
  160. finalResult.menu.button.Add(subButton);
  161. foreach (var sb in fullButton["sub_button"] as object[])
  162. {
  163. subButton.sub_button.Add(GetSingleButtonFromJsonObject(sb as Dictionary<string, object>));
  164. }
  165. }
  166. }
  167. }
  168. else if (fullResult != null && fullResult.ContainsKey("errmsg"))
  169. {
  170. //菜单不存在
  171. throw new ErrorJsonResultException(fullResult["errmsg"] as string, null, null);
  172. }
  173. }
  174. catch (ErrorJsonResultException ex)
  175. {
  176. finalResult = null;
  177. //如果没有惨淡会返回错误代码:46003:menu no exist
  178. }
  179. catch (Exception ex)
  180. {
  181. //其他异常
  182. finalResult = null;
  183. }
  184. return finalResult;
  185. }
  186. /// <summary>
  187. /// 获取当前菜单,如果菜单不存在,将返回null
  188. /// </summary>
  189. /// <param name="accessTokenOrAppId">AccessToken或AppId(推荐使用AppId,需要先注册)</param>
  190. /// <returns></returns>
  191. [ApiBind(NeuChar.PlatformType.WeChat_OfficialAccount, "CommonApi.GetMenu", true)]
  192. public static GetMenuResult GetMenu(string accessTokenOrAppId)
  193. {
  194. return ApiHandlerWapper.TryCommonApi(accessToken =>
  195. {
  196. var url = string.Format(Config.ApiMpHost + "/cgi-bin/menu/get?access_token={0}", accessToken.AsUrlData());
  197. var jsonString = RequestUtility.HttpGet(url, Encoding.UTF8);
  198. //var finalResult = GetMenuFromJson(jsonString);
  199. GetMenuResult finalResult;
  200. try
  201. {
  202. #if NET35 || NET40 || NET45
  203. JavaScriptSerializer js = new JavaScriptSerializer();
  204. var jsonResult = js.Deserialize<GetMenuResultFull>(jsonString);
  205. #else
  206. var jsonResult = Newtonsoft.Json.JsonConvert.DeserializeObject<GetMenuResultFull>(jsonString);
  207. #endif
  208. if (jsonResult.menu == null || jsonResult.menu.button.Count == 0)
  209. {
  210. throw new WeixinMenuException(jsonResult.errmsg);
  211. }
  212. finalResult = GetMenuFromJsonResult(jsonResult, new ButtonGroup());
  213. }
  214. catch (WeixinMenuException)
  215. {
  216. throw;
  217. //finalResult = null;
  218. }
  219. catch (Exception)
  220. {
  221. throw;
  222. }
  223. return finalResult;
  224. }, accessTokenOrAppId);
  225. }
  226. #endregion
  227. /// <summary>
  228. /// 删除菜单
  229. /// </summary>
  230. /// <param name="accessTokenOrAppId"></param>
  231. /// <returns></returns>
  232. [ApiBind(NeuChar.PlatformType.WeChat_OfficialAccount, "CommonApi.DeleteMenu", true)]
  233. public static WxJsonResult DeleteMenu(string accessTokenOrAppId)
  234. {
  235. return ApiHandlerWapper.TryCommonApi(accessToken =>
  236. {
  237. var url = string.Format(Config.ApiMpHost + "/cgi-bin/menu/delete?access_token={0}", accessToken.AsUrlData());
  238. return CommonJsonSend.Send<WxJsonResult>(null, url, null, CommonJsonSendType.GET);
  239. }, accessTokenOrAppId);
  240. }
  241. #region 同步方法
  242. /// <summary>
  243. /// 获取自定义菜单配置接口
  244. /// </summary>
  245. /// <param name="accessTokenOrAppId"></param>
  246. /// <param name="timeOut"></param>
  247. /// <returns></returns>
  248. [ApiBind(NeuChar.PlatformType.WeChat_OfficialAccount, "CommonApi.GetCurrentSelfMenuInfo", true)]
  249. public static SelfMenuConfigResult GetCurrentSelfMenuInfo(string accessTokenOrAppId, int timeOut = Config.TIME_OUT)
  250. {
  251. return ApiHandlerWapper.TryCommonApi(accessToken =>
  252. {
  253. var url = string.Format(Config.ApiMpHost + "/cgi-bin/get_current_selfmenu_info?access_token={0}", accessToken.AsUrlData());
  254. return CommonJsonSend.Send<SelfMenuConfigResult>(null, url, null, CommonJsonSendType.GET, timeOut: timeOut);
  255. }, accessTokenOrAppId);
  256. }
  257. #endregion
  258. #if !NET35 && !NET40
  259. #region 异步方法
  260. /// <summary>
  261. /// 【异步方法】获取自定义菜单配置接口
  262. /// </summary>
  263. /// <param name="accessTokenOrAppId"></param>
  264. /// <param name="timeOut"></param>
  265. /// <returns></returns>
  266. [ApiBind(NeuChar.PlatformType.WeChat_OfficialAccount, "CommonApi.GetCurrentSelfMenuInfo", true)]
  267. public static async Task<SelfMenuConfigResult> GetCurrentSelfMenuInfoAsync(string accessTokenOrAppId, int timeOut = Config.TIME_OUT)
  268. {
  269. return await ApiHandlerWapper.TryCommonApiAsync(async accessToken =>
  270. {
  271. var url = string.Format(Config.ApiMpHost + "/cgi-bin/get_current_selfmenu_info?access_token={0}", accessToken.AsUrlData());
  272. return await CommonJsonSend.SendAsync<SelfMenuConfigResult>(null, url, null, CommonJsonSendType.GET, timeOut: timeOut);
  273. }, accessTokenOrAppId);
  274. }
  275. #endregion
  276. #endif
  277. }
  278. }