本次更新同时影响以下所有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