zoukankan      html  css  js  c++  java
  • 【重要更新】Senparc.Weixin SDK v4.4 升级说明

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

      本次更新重构了Senparc.Weixin整个框架内几乎所有的CommonJsonSend类,并且为SDK添加了Debug状态用于调试状态下自动输出日志,以及其他异常类型相关的升级。

      具体升级内容如下:

    Senparc.Weixin.dll

    1. 添加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:
      {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状态请看下面。

    2. 添加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状态已关闭。");
              }
          }
      }



    3. 添加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
          }
      }
    4. 修改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 }
      View Code

    Senparc.Weixin.MP.dll

    1. 弃用CommonJsonSend类(如果还继续使用不会出错,编译时候会提示过期),将在今后版本中彻底删除。
    2. 优化JsSdkApi相关接口。

    Senparc.Weixin.Open.dll

    1. 弃用CommonJsonSend类(如果还继续使用不会出错,编译时候会提示过期),将在今后版本中彻底删除。
    2. 完善ComponentContainer及ComponentApi下的接口。
    3. 完善Component及OAuth相关接口,提供了更加自动化的接口获取。
    4. 比如以前按照官方的流程我们需要走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

  • 相关阅读:
    Codechef Observing the Tree
    bzoj 1367: [Baltic2004]sequence
    bzoj 2375: 疯狂的涂色
    bzoj 1455: 罗马游戏
    codevs 1029 遍历问题
    HNOI2004 宠物收养所 (Treap)
    [ZJOI2007] 报表统计
    bzoj 3261: 最大异或和 (可持久化trie树)
    codevs 1001 舒适的路线
    Codechef Dynamic Trees and Queries
  • 原文地址:https://www.cnblogs.com/szw/p/weixin-sdk-update-4_4.html
Copyright © 2011-2022 走看看