本次更新同時影響以下所有Senparc.Weixin相關版本的dll:
- Senparc.Weixin.dll 升級到 v4.4.2(重要)
- Senparc.Weixin.MP.dll 升級到 v13.4.0(重要)
- Senparc.Weixin.MP.MvcExtension.dll 無需升級
- Senparc.Weixin.Open 升級到 v1.5.3(重要)
- Senparc.Weixin.QY.dll 升級到 v3.2.0(重要)
源代碼:https://github.com/JeffreySu/WeiXinMPSDK
Demo:http://weixin.senparc.com/
Nuget:
- https://www.nuget.org/packages/Senparc.Weixin
- https://www.nuget.org/packages/Senparc.Weixin.MP
- https://www.nuget.org/packages/Senparc.Weixin.Open
- https://www.nuget.org/packages/Senparc.Weixin.QY
本次更新重構了Senparc.Weixin整個框架內幾乎所有的CommonJsonSend類,並且為SDK添加了Debug狀態用於調試狀態下自動輸出日志,以及其他異常類型相關的升級。
具體升級內容如下:
Senparc.Weixin.dll
- 添加WeixinTrace類,用於輸出完整的API請求等日志。
1 /*---------------------------------------------------------------- 2 Copyright (C) 2015 Senparc 3 4 文件名:WeixinTrace.cs 5 文件功能描述:跟蹤日志相關 6 7 8 創建標識:Senparc - 20151012 9 10 ----------------------------------------------------------------*/ 11 12 using System; 13 using System.Collections.Generic; 14 using System.Diagnostics; 15 using System.IO; 16 using System.Linq; 17 using System.Text; 18 using System.Threading.Tasks; 19 using Senparc.Weixin.Exceptions; 20 21 namespace Senparc.Weixin 22 { 23 /// <summary> 24 /// 微信日志跟蹤 25 /// </summary> 26 public static class WeixinTrace 27 { 28 private static TraceListener _traceListener = null; 29 private static readonly object TraceLock = new object(); 30 31 internal static void Open() 32 { 33 Close(); 34 lock (TraceLock) 35 { 36 var logDir = System.AppDomain.CurrentDomain.BaseDirectory + "App_Data"; 37 string logFile = Path.Combine(logDir, "SenparcWeixinTrace.log"); 38 System.IO.TextWriter logWriter = new System.IO.StreamWriter(logFile, true); 39 _traceListener = new TextWriterTraceListener(logWriter); 40 System.Diagnostics.Trace.Listeners.Add(_traceListener); 41 System.Diagnostics.Trace.AutoFlush = true; 42 } 43 } 44 45 internal static void Close() 46 { 47 lock (TraceLock) 48 { 49 if (_traceListener != null && System.Diagnostics.Trace.Listeners.Contains(_traceListener)) 50 { 51 _traceListener.Close(); 52 System.Diagnostics.Trace.Listeners.Remove(_traceListener); 53 } 54 } 55 } 56 57 /// <summary> 58 /// 統一時間格式 59 /// </summary> 60 private static void TimeLog() 61 { 62 Log(string.Format("[{0}]", DateTime.Now)); 63 } 64 65 private static void Unindent() 66 { 67 lock (TraceLock) 68 { 69 System.Diagnostics.Trace.Unindent(); 70 } 71 } 72 73 private static void Indent() 74 { 75 lock (TraceLock) 76 { 77 System.Diagnostics.Trace.Indent(); 78 } 79 } 80 81 private static void Flush() 82 { 83 lock (TraceLock) 84 { 85 System.Diagnostics.Trace.Flush(); 86 } 87 } 88 89 private static void LogBegin(string title = null) 90 { 91 Open(); 92 Log(title == null ? "" : String.Format("[{0}]", title)); 93 TimeLog(); 94 Indent(); 95 } 96 97 /// <summary> 98 /// 記錄日志 99 /// </summary> 100 /// <param name="message"></param> 101 public static void Log(string message) 102 { 103 lock (TraceLock) 104 { 105 System.Diagnostics.Trace.WriteLine(message); 106 } 107 } 108 109 private static void LogEnd() 110 { 111 Unindent(); 112 Flush(); 113 Close(); 114 } 115 116 /// <summary> 117 /// API請求日志 118 /// </summary> 119 /// <param name="url"></param> 120 /// <param name="returnText"></param> 121 public static void SendLog(string url, string returnText) 122 { 123 if (!Config.IsDebug) 124 { 125 return; 126 } 127 128 LogBegin("接口調用"); 129 Log(string.Format("URL:{0}", url)); 130 Log(string.Format("Result:\r\n{0}", returnText)); 131 LogEnd(); 132 } 133 134 /// <summary> 135 /// ErrorJsonResultException 日志 136 /// </summary> 137 /// <param name="ex"></param> 138 public static void ErrorJsonResultExceptionLog(ErrorJsonResultException ex) 139 { 140 if (!Config.IsDebug) 141 { 142 return; 143 } 144 145 LogBegin("ErrorJsonResultException"); 146 Log(string.Format("URL:{0}", ex.Url)); 147 Log(string.Format("errcode:{0}", ex.JsonResult.errcode)); 148 Log(string.Format("errmsg:{0}", ex.JsonResult.errmsg)); 149 LogEnd(); 150 } 151 } 152 }
在WeixinTrace中,已經提供了兩個自帶的日志記錄方法,供SDK的擴展庫使用:
- SendLog():API請求日志
日志格式如下:[接口調用]
[2015/10/14 10:40:27]
URL:https://api.weixin.qq.com/cgi-bin/menu/create?access_token=-3w2HMV7R1r_YWAryHtoVDzOHffPUUe4Cf48
Result:
{"errcode":0,"errmsg":"ok"} - ErrorJsonResultExceptionLog():每次創建ErrorJsonResultException的時候記錄該異常的信息
日志格式如下:[ErrorJsonResultException]
[2015/10/14 11:13:49]
URL:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=[AppId]&secret=[AppSecret]
errcode:獲取access_token時AppSecret錯誤或者access_token無效
errmsg:invalid credential, access_token is invalid or not latest hint: [weMY8a0430vr18]
這些日志默認被記錄在網站(或應用)的App_Data/SenparcWeixinTrace.log文件中。
日志只有在SDK出於Debug狀態下才會記錄,如何開啟Debug狀態請看下面。
- SendLog():API請求日志
- 添加Config.IsDebug屬性。當Config.IsDebug為true時,WeixinTrace的日志記錄功能才會被開啟,否則即使調用方法,日志也不會被記錄。
建議在調試階段使用此功能,正式發布的時候關閉。Debug狀態可以在程序啟動的時候或程序中的任意位置執行,如:
namespace Senparc.Weixin.MP.Sample { public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { //... Senparc.Weixin.Config.IsDebug = true;//這里設為Debug狀態時,/App_Data/目錄下會生成日志文件記錄所有的API請求日志,正式發布版本建議關閉 } } }
或者像這樣在管理員權限下面(建議)做一個開關:
namespace Senparc.Weixin.MP.Sample.Controllers { public class HomeController : BaseController { public ActionResult DebugOpen() { Senparc.Weixin.Config.IsDebug = true; return Content("Debug狀態已打開。"); } public ActionResult DebugClose() { Senparc.Weixin.Config.IsDebug = false; return Content("Debug狀態已關閉。"); } } }
- 添加CommonAPIs/CommonJsonSend.cs,舊版本的Senparc.Weixin.MP及Senparc.Weixin.Open對應文件刪除,統一到Senparc.Weixin中。
.net 4.5版本的代碼如下(.net 4.0版本沒有異步功能):
/*---------------------------------------------------------------- Copyright (C) 2015 Senparc 文件名:CommonJsonSend.cs 文件功能描述:通過CommonJsonSend中的方法調用接口 創建標識:Senparc - 20151012 ----------------------------------------------------------------*/ using System; using System.IO; using System.Text; using System.Threading.Tasks; using Senparc.Weixin.Entities; using Senparc.Weixin.Exceptions; using Senparc.Weixin.Helpers; using Senparc.Weixin.HttpUtility; namespace Senparc.Weixin.CommonAPIs { /// <summary> /// CommonJsonSend /// </summary> public static class CommonJsonSend { #region 同步請求 /// <summary> /// 向需要AccessToken的API發送消息的公共方法 /// </summary> /// <param name="accessToken">這里的AccessToken是通用接口的AccessToken,非OAuth的。如果不需要,可以為null,此時urlFormat不要提供{0}參數</param> /// <param name="urlFormat"></param> /// <param name="data">如果是Get方式,可以為null</param> /// <param name="sendType"></param> /// <param name="timeOut">代理請求超時時間(毫秒)</param> /// <param name="jsonSetting"></param> /// <returns></returns> public static WxJsonResult Send(string accessToken, string urlFormat, object data, CommonJsonSendType sendType = CommonJsonSendType.POST, int timeOut = Config.TIME_OUT, bool checkValidationResult = false, JsonSetting jsonSetting = null) { return Send<WxJsonResult>(accessToken, urlFormat, data, sendType, timeOut); } /// <summary> /// 向需要AccessToken的API發送消息的公共方法 /// </summary> /// <param name="accessToken">這里的AccessToken是通用接口的AccessToken,非OAuth的。如果不需要,可以為null,此時urlFormat不要提供{0}參數</param> /// <param name="urlFormat">用accessToken參數填充{0}</param> /// <param name="data">如果是Get方式,可以為null</param> /// <param name="sendType"></param> /// <param name="timeOut">代理請求超時時間(毫秒)</param> /// <param name="checkValidationResult"></param> /// <param name="jsonSetting"></param> /// <returns></returns> public static T Send<T>(string accessToken, string urlFormat, object data, CommonJsonSendType sendType = CommonJsonSendType.POST, int timeOut = Config.TIME_OUT, bool checkValidationResult = false, JsonSetting jsonSetting = null) { //TODO:此方法可以設定一個日志記錄開關 try { var url = string.IsNullOrEmpty(accessToken) ? urlFormat : string.Format(urlFormat, accessToken); switch (sendType) { case CommonJsonSendType.GET: return Get.GetJson<T>(url); case CommonJsonSendType.POST: SerializerHelper serializerHelper = new SerializerHelper(); var jsonString = serializerHelper.GetJsonString(data); using (MemoryStream ms = new MemoryStream()) { var bytes = Encoding.UTF8.GetBytes(jsonString); ms.Write(bytes, 0, bytes.Length); ms.Seek(0, SeekOrigin.Begin); return Post.PostGetJson<T>(url, null, ms, timeOut: timeOut, checkValidationResult: checkValidationResult); } //TODO:對於特定的錯誤類型自動進行一次重試,如40001(目前的問題是同樣40001會出現在不同的情況下面) default: throw new ArgumentOutOfRangeException("sendType"); } } catch (ErrorJsonResultException ex) { ex.Url = urlFormat; throw; } } #endregion #region 異步請求 /// <summary> /// 向需要AccessToken的API發送消息的公共方法 /// </summary> /// <param name="accessToken">這里的AccessToken是通用接口的AccessToken,非OAuth的。如果不需要,可以為null,此時urlFormat不要提供{0}參數</param> /// <param name="urlFormat"></param> /// <param name="data">如果是Get方式,可以為null</param> /// <param name="timeOut">代理請求超時時間(毫秒)</param> /// <returns></returns> public static async Task<WxJsonResult> SendAsync(string accessToken, string urlFormat, object data, CommonJsonSendType sendType = CommonJsonSendType.POST, int timeOut = Config.TIME_OUT) { return await SendAsync<WxJsonResult>(accessToken, urlFormat, data, sendType, timeOut); } /// <summary> /// 向需要AccessToken的API發送消息的公共方法 /// </summary> /// <param name="accessToken">這里的AccessToken是通用接口的AccessToken,非OAuth的。如果不需要,可以為null,此時urlFormat不要提供{0}參數</param> /// <param name="urlFormat"></param> /// <param name="data">如果是Get方式,可以為null。在POST方式中將被轉為JSON字符串提交</param> /// <param name="sendType">發送類型,POST或GET,默認為POST</param> /// <param name="timeOut">代理請求超時時間(毫秒)</param> /// <param name="checkValidationResult">驗證服務器證書回調自動驗證</param> /// <param name="jsonSetting">JSON字符串生成設置</param> /// <returns></returns> public static async Task<T> SendAsync<T>(string accessToken, string urlFormat, object data, CommonJsonSendType sendType = CommonJsonSendType.POST, int timeOut = Config.TIME_OUT, bool checkValidationResult = false, JsonSetting jsonSetting = null ) { try { var url = string.IsNullOrEmpty(accessToken) ? urlFormat : string.Format(urlFormat, accessToken); switch (sendType) { case CommonJsonSendType.GET: return await Get.GetJsonAsync<T>(url); case CommonJsonSendType.POST: SerializerHelper serializerHelper = new SerializerHelper(); var jsonString = serializerHelper.GetJsonString(data, jsonSetting); using (MemoryStream ms = new MemoryStream()) { var bytes = Encoding.UTF8.GetBytes(jsonString); await ms.WriteAsync(bytes, 0, bytes.Length); ms.Seek(0, SeekOrigin.Begin); return await Post.PostGetJsonAsync<T>(url, null, ms, timeOut: timeOut, checkValidationResult: checkValidationResult); } default: throw new ArgumentOutOfRangeException("sendType"); } } catch (ErrorJsonResultException ex) { ex.Url = urlFormat; throw; } } #endregion } }
- 修改ErrorJsonResultException類,添加Url屬性,方便開發者跟蹤異常來自哪個URL(通常是請求接口)。
1 /*---------------------------------------------------------------- 2 Copyright (C) 2015 Senparc 3 4 文件名:ErrorJsonResultException.cs 5 文件功能描述:JSON返回錯誤代碼(比如token_access相關操作中使用)。 6 7 8 創建標識:Senparc - 20150211 9 10 修改標識:Senparc - 20150303 11 修改描述:整理接口 12 ----------------------------------------------------------------*/ 13 14 using System; 15 using Senparc.Weixin.Entities; 16 17 namespace Senparc.Weixin.Exceptions 18 { 19 /// <summary> 20 /// JSON返回錯誤代碼(比如token_access相關操作中使用)。 21 /// </summary> 22 public class ErrorJsonResultException : WeixinException 23 { 24 public WxJsonResult JsonResult { get; set; } 25 public string Url { get; set; } 26 27 /// <summary> 28 /// 29 /// </summary> 30 /// <param name="message"></param> 31 /// <param name="inner"></param> 32 /// <param name="jsonResult"></param> 33 /// <param name="url"></param> 34 public ErrorJsonResultException(string message, Exception inner, WxJsonResult jsonResult, string url = null) 35 : base(message, inner) 36 { 37 JsonResult = jsonResult; 38 Url = url; 39 40 WeixinTrace.ErrorJsonResultExceptionLog(this); 41 } 42 } 43 }
Senparc.Weixin.MP.dll
- 棄用CommonJsonSend類(如果還繼續使用不會出錯,編譯時候會提示過期),將在今后版本中徹底刪除。
- 優化JsSdkApi相關接口。
Senparc.Weixin.Open.dll
- 棄用CommonJsonSend類(如果還繼續使用不會出錯,編譯時候會提示過期),將在今后版本中徹底刪除。
- 完善ComponentContainer及ComponentApi下的接口。
- 完善Component及OAuth相關接口,提供了更加自動化的接口獲取。
- 比如以前按照官方的流程我們需要走4步才可以獲取到queryAuth的授權(微信官方文檔:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318587&token=f6aa7cd0d4ffc0820a9d694daa06fb841123fd2e&lang=zh_CN):
1 string openTicket = OpenTicketHelper.GetOpenTicket(component_AppId); 2 var component_access_token = Open.CommonAPIs.CommonApi.GetComponentAccessToken(component_AppId, component_Secret, openTicket).component_access_token; 3 ComponentAccessToken = component_access_token; 4 var oauthResult = Open.ComponentAPIs.ComponentApi.QueryAuth(component_access_token, component_AppId, auth_code);
現在有了ComponentContainer,只需要一步,整個過程(包括所有會過期的token,ComponentContainer都會自動管理):
1 var queryAuthResult = ComponentContainer.GetQueryAuthResult(component_AppId, auth_code);
還有比這個更爽的嗎?
Sample中的Demo也已經同步更新,大家現在可以在這個頁面打開測試:http://weixin.senparc.com/OpenOAuth/JumpToMpOAuth
Senparc.Weixin.QY.dll
常規升級,由於企業號的錯誤類型等,和公眾號、開放平台不一樣,所以本次更新沒有將企業號的CommonJsonSend集結到Senparc.Weixin.dll中,仍然獨立存在。
系列教程索引:http://www.cnblogs.com/szw/archive/2013/05/14/weixin-course-index.html