【重要更新】Senparc.Weixin SDK v4.3.3升級說明


  為了更好地適應微信越來越快的API更新速度和越來越多的API數量,本次Senparc.Weixin.dll v4.3.3對一些通用功能進行了深度的重構。

  本次更新同時影響以下所有Senparc.Weixin相關版本的dll:

  • Senparc.Weixin.dll 升級到 v4.3.3
  • Senparc.Weixin.MP.dll 升級到 v13.3.0(重要)
  • Senparc.Weixin.MP.MvcExtension.dll 升級到 v1.4.1
  • Senparc.Weixin.Open 升級到 v1.4.1(重要)
  • Senparc.Weixin.QY.dll 升級到 v3.1.1(尚處內測中,近期發布,重要)

  下面詳細介紹一下本次更新的內容,及小於上述版本的dll的項目升級方式(已經盡量確保方法的兼容性,基本上只需要批量替換)。

 

 

Senparc.Weixin.dll

  升級內容:

  1、為JSON字符串輸出過程中,過濾值為null的屬性,提供了解決方案。

  涉及到的文件如下圖所示:

  其中:SerializerHelper.cs為升級,Conventers文件夾為新增的兩個用於支持SerializerHelper的轉換器。

  ExpandoJsonConverter.cs用於提供ExpandoObject類型到JSON字符串的轉換:

/*----------------------------------------------------------------
    Copyright (C) 2015 Senparc
    
    文件名:ExpandoJsonConverter.cs
    文件功能描述:Expando-JSON字符串轉換
    
    
    創建標識:Senparc - 20151002
    
----------------------------------------------------------------*/
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Dynamic;
using System.Web.Script.Serialization;

namespace Senparc.Weixin.Helpers
{
    /// <summary>
    /// Allows JSON serialization of Expando objects into expected results (e.g., "x: 1, y: 2") instead of the default dictionary serialization.
    /// </summary>
    public class ExpandoJsonConverter : JavaScriptConverter
    {
        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
        {
            // See source code link for this extension method at the bottom of this post (/Helpers/IDictionaryExtensions.cs)
            return dictionary.ToExpando();
        }

        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
        {
            var result = new Dictionary<string, object>();
            var dictionary = obj as IDictionary<string, object>;
            foreach (var item in dictionary)
                result.Add(item.Key, item.Value);
            return result;
        }

        public override IEnumerable<Type> SupportedTypes
        {
            get
            {
                return new ReadOnlyCollection<Type>(new Type[] { typeof(ExpandoObject) });
            }
        }
    }
}
View Code

  WeixinJsonConventer.cs用於提供微信中部分字段為null時,整個屬性都不輸出的配置方法:

/*----------------------------------------------------------------
    Copyright (C) 2015 Senparc
    
    文件名:WeixinJsonConventer.cs
    文件功能描述:微信JSON字符串轉換
    
    
    創建標識:Senparc - 20150930
    
----------------------------------------------------------------*/

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Web.Script.Serialization;
using Senparc.Weixin.Entities;

namespace Senparc.Weixin.Helpers
{
    /// <summary>
    /// JSON輸出設置
    /// </summary>
    public class JsonSetting
    {
        /// <summary>
        /// 是否忽略當前類型以及具有IJsonIgnoreNull接口,且為Null值的屬性。如果為true,符合此條件的屬性將不會出現在Json字符串中
        /// </summary>
        public bool IgnoreNulls { get; set; }
        /// <summary>
        /// 需要特殊忽略null值的屬性名稱
        /// </summary>
        public List<string> PropertiesToIgnore { get; set; }
        /// <summary>
        /// 指定類型(Class,非Interface)下的為null屬性不生成到Json中
        /// </summary>
        public List<Type> TypesToIgnore { get; set; }


        /// <summary>
        /// JSON輸出設置 構造函數
        /// </summary>
        /// <param name="ignoreNulls">是否忽略當前類型以及具有IJsonIgnoreNull接口,且為Null值的屬性。如果為true,符合此條件的屬性將不會出現在Json字符串中</param>
        /// <param name="propertiesToIgnore">需要特殊忽略null值的屬性名稱</param>
        /// <param name="typesToIgnore">指定類型(Class,非Interface)下的為null屬性不生成到Json中</param>
        public JsonSetting(bool ignoreNulls = false, List<string> propertiesToIgnore = null, List<Type> typesToIgnore = null)
        {
            IgnoreNulls = ignoreNulls;
            PropertiesToIgnore = propertiesToIgnore ?? new List<string>();
            TypesToIgnore = typesToIgnore ?? new List<Type>();
        }
    }

    /// <summary>
    /// 微信JSON轉換器
    /// </summary>
    public class WeixinJsonConventer : JavaScriptConverter
    {
        private readonly JsonSetting _jsonSetting;
        private readonly Type _type;

        public WeixinJsonConventer(Type type, JsonSetting jsonSetting = null)
        {
            this._jsonSetting = jsonSetting ?? new JsonSetting();
            this._type = type;
        }

        public override IEnumerable<Type> SupportedTypes
        {
            get
            {
                var typeList = new List<Type>(new[] { typeof(IJsonIgnoreNull)/*,typeof(JsonIgnoreNull)*/ });

                if (_jsonSetting.TypesToIgnore.Count > 0)
                {
                    typeList.AddRange(_jsonSetting.TypesToIgnore);
                }

                if (_jsonSetting.IgnoreNulls)
                {
                    typeList.Add(_type);
                }

                return new ReadOnlyCollection<Type>(typeList);
            }
        }

        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
        {
            var result = new Dictionary<string, object>();
            if (obj == null)
            {
                return result;
            }

            var properties = obj.GetType().GetProperties();
            foreach (var propertyInfo in properties)
            {
                if (!this._jsonSetting.PropertiesToIgnore.Contains(propertyInfo.Name))
                {
                    bool ignoreProp = propertyInfo.IsDefined(typeof(ScriptIgnoreAttribute), true);

                    if ((this._jsonSetting.IgnoreNulls || ignoreProp) && propertyInfo.GetValue(obj, null) == null)
                    {
                        continue;
                    }

                    result.Add(propertyInfo.Name, propertyInfo.GetValue(obj, null));
                }
            }
            return result;
        }

        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
        {
            throw new NotImplementedException(); //Converter is currently only used for ignoring properties on serialization
        }
    }
}
View Code

  WeixinJsonConventer提供了多種過濾的方式:

  • 過濾當前數據對象類型(或任一指定類型)下,所有為null的屬性(根據JsonSetting.IgnoreNulls配置)
  • 過濾當前數據對象類型(或任一指定類型)下,所有為null,且指定名稱的屬性(根據JsonSetting.PropertiesToIgnore配置)
  • 過濾指定類型下(可多個),所有為null的屬性(根據JsonSetting.TypesToIgnore配置)
  • 過濾實現了IJsonIgnoreNull接口的類型下,,所有為null的屬性(自動)

  以上通過配置或自動的過濾規則只要滿足其中一條即被過濾)

  目前已經發現的需要過濾null的地方不多(猜測是微信的bug),通常情況下此方法不需要在SDK外部使用,目前SDK內部部分接口已經在使用,如創建卡券的接口:

 1         /// <summary>
 2         /// 創建卡券
 3         /// </summary>
 4         /// <param name="accessTokenOrAppId"></param>
 5         /// <param name="cardInfo">創建卡券需要的數據,格式可以看CardCreateData.cs</param>
 6         /// <param name="timeOut">代理請求超時時間(毫秒)</param>
 7         /// <returns></returns>
 8         public static CardCreateResultJson CreateCard(string accessTokenOrAppId, BaseCardInfo cardInfo, int timeOut = Config.TIME_OUT)
 9         {
10             return ApiHandlerWapper.TryCommonApi(accessToken =>
11             {
12                 var urlFormat = string.Format("https://api.weixin.qq.com/card/create?access_token={0}", accessToken);
13 
14                 CardCreateInfo cardData = null;
15                 CardType cardType = cardInfo.GetCardType();
16 
17                 switch (cardType)
18                 {
19                     ...
20                 }
21 
22                 var jsonSetting = new JsonSetting(true, null,
23                     new List<Type>()
24                     {
25                         //typeof (Modify_Msg_Operation),
26                         //typeof (CardCreateInfo),
27                         typeof (Card_BaseInfoBase)//過濾Modify_Msg_Operation主要起作用的是這個
28                     });
29 
30                 var result = CommonJsonSend.Send<CardCreateResultJson>(null, urlFormat, cardData, timeOut: timeOut,
31                    //針對特殊字段的null值進行過濾
32                    jsonSetting: jsonSetting);
33                 return result;
34 
35             }, accessTokenOrAppId);
36         }        

  第一步:定義JsonSetting,確定需要過濾的規則。

  第二步:將JsonSetting對象傳入CommonJsonSend.Send()方法。

 

  2、增加Containers基礎功能:用於作為其他子庫中所有Container(如AccessTokenContainer)的基類,並提供配套的ContainerBag(信息包)基類。

  如下圖所示:

  BaseContainerBag.cs 用於提供信息報的基類:

/*----------------------------------------------------------------
    Copyright (C) 2015 Senparc
    
    文件名:BaseContainerBag.cs
    文件功能描述:微信容器接口中的封裝Value(如Ticket、AccessToken等數據集合)
    
    
    創建標識:Senparc - 20151003
    
----------------------------------------------------------------*/

namespace Senparc.Weixin.Containers
{
    /// <summary>
    /// IBaseContainerBag
    /// </summary>
    public interface IBaseContainerBag
    {
        string Key { get; set; }
    }

    /// <summary>
    /// BaseContainer容器中的Value類型
    /// </summary>
    public class BaseContainerBag : IBaseContainerBag
    {
        /// <summary>
        /// 通常為AppId
        /// </summary>
        public string Key { get; set; }
    }

}

 

  BaseContainer.cs用於提供所有Container容器的基類:

/*----------------------------------------------------------------
    Copyright (C) 2015 Senparc
    
    文件名:WeixinContainer.cs
    文件功能描述:微信容器(如Ticket、AccessToken)
    
    
    創建標識:Senparc - 20151003
    
----------------------------------------------------------------*/

using System;
using System.Collections.Generic;
using System.Linq;

namespace Senparc.Weixin.Containers
{
    /// <summary>
    /// 微信容器接口(如Ticket、AccessToken)
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class BaseContainer<T> where T : IBaseContainerBag, new()
    {
        /// <summary>
        /// 所有數據集合的列表
        /// </summary>
        private static readonly Dictionary<Type, Dictionary<string, T>> _collectionList = new Dictionary<Type, Dictionary<string, T>>();

        /// <summary>
        /// 獲取當前容器的數據項集合
        /// </summary>
        /// <returns></returns>
        protected static Dictionary<string, T> ItemCollection
        {
            get
            {
                if (!_collectionList.ContainsKey(typeof(T)))
                {
                    _collectionList[typeof(T)] = new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase);
                }
                return _collectionList[typeof(T)];
            }
        }

        /// <summary>
        /// 獲取完整的數據集合的列表(建議不要進行任何修改操作)
        /// </summary>
        /// <returns></returns>
        public static Dictionary<Type, Dictionary<string, T>> GetCollectionList()
        {
            return _collectionList;
        }

        /// <summary>
        /// 獲取所有容器內已經注冊的項目
        /// (此方法將會遍歷Dictionary,當數據項很多的時候效率會明顯降低)
        /// </summary>
        /// <returns></returns>
        public static List<T> GetAllItems()
        {
            return ItemCollection.Select(z => z.Value).ToList();
        }

        /// <summary>
        /// 嘗試獲取某一項Bag
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static T TryGetItem(string key)
        {
            if (ItemCollection.ContainsKey(key))
            {
                return ItemCollection[key];
            }

            return default(T);
        }

        /// <summary>
        /// 嘗試獲取某一項Bag中的具體某個屬性
        /// </summary>
        /// <param name="key"></param>
        /// <param name="property">具體某個屬性</param>
        /// <returns></returns>
        public static K TryGetItem<K>(string key, Func<T, K> property)
        {
            if (ItemCollection.ContainsKey(key))
            {
                var item = ItemCollection[key];
                return property(item);
            }
            return default(K);
        }

        /// <summary>
        /// 更新數據項
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value">為null時刪除該項</param>
        public static void Update(string key, T value)
        {
            if (value == null)
            {
                ItemCollection.Remove(key);
            }
            else
            {
                ItemCollection[key] = value;
            }
        }

        /// <summary>
        /// 更新數據項
        /// </summary>
        /// <param name="key"></param>
        /// <param name="partialUpdate">為null時刪除該項</param>
        public static void Update(string key, Action<T> partialUpdate)
        {
            if (partialUpdate == null)
            {
                ItemCollection.Remove(key);//移除對象
            }
            else
            {
                if (!ItemCollection.ContainsKey(key))
                {
                    ItemCollection[key] = new T()
                    {
                        Key = key//確保這一項Key已經被記錄
                    };
                }
                partialUpdate(ItemCollection[key]);//更新對象
            }
        }

        /// <summary>
        /// 檢查Key是否已經注冊
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static bool CheckRegistered(string key)
        {
            return ItemCollection.ContainsKey(key);
        }
    }
}

  以上兩個基類將被用於其他所有子庫(MP、Open、QY等)的各種Container中,為其提供統一數據管理能力和集中的緩存存儲支持。

 

  

Senparc.Weixin.MP.dll

  升級內容:

  1、刪除JsApiTicketContainer,合並入AccessTokenContainer,同時以BaseContainer為基類重構AccessTokenContainer。

  之前我們需要這樣分別注冊兩次AccessTokenContainer和JsApiTicketContainer,分別注冊兩個容器:

            //全局只需注冊一次
            AccessTokenContainer.Register(_appId, _appSecret);

            //全局只需注冊一次
            JsApiTicketContainer.Register(_appId, _appSecret);

 

  現在只需要第一行就行了:

            //全局只需注冊一次
            AccessTokenContainer.Register(_appId, _appSecret);

 

  隨着JsApiTicketContainer的合並,AccessTokenContainer內部的方法名也進行了調整,具體如下:

原方法名稱 現方法名稱
AccessTokenContainer.Register() 不變
AccessTokenContainer.GetFirstOrDefaultAppId() 不變
AccessTokenContainer.TryGetToken() AccessTokenContainer.TryGetAccessToken()
AccessTokenContainer.GetToken() AccessTokenContainer.GetAccessToken()
AccessTokenContainer.GetTokenResult() AccessTokenContainer.GetAccessTokenResult()
JsApiTicketContainer.TryGetTicket() AccessTokenContainer.TryGetJsApiTicket()
JsApiTicketContainer.GetTicket() AccessTokenContainer.GetJsApiTicket()
8 JsApiTicketContainer.GetTicketResult() AccessTokenContainer.GetJsApiTicketResult()

  所有相關的升級只需要替換上述對應的方法名稱即可,參數不需要變化。其中最常用的應該是#4和#7。

  升級方法:

  第一步:升級SDK(方法略,建議用nuget)

  第二步:編譯。會出一些錯,隨便找到一個,如之前使用了AccessTokenContainer.GetToken()的方法,按Ctrl+F進行替換:

  第三步:替換完成、保存:

  對於JsApiTicketContainer相關的替換也參考以上步驟:

 

  2、對接口進行常規升級,包括JSON字符串過濾null值的處理。

 

 

Senparc.Weixin.MP.MvcExtension.dll

  升級內容:

  fixbug(重要)。

 

 

Senparc.Weixin.Open.dll

  升級內容:

  1、創建ComponentContainer。

  ComponentContainer是用於存放第三方平台信息的容器,Key為ComponentId,信息包為ComponentBag。

  ComponentContainer提供了所有第三方平台需要用到的緩存信息以及獲取方法,包括:ComponentVerifyTicketPreAuthCodeAuthorizerAccessToken

  其中PreAuthCode相關的對象由原來的PreAuthCodeContainer合並而來,PreAuthCodeContainer現已刪除(升級方法參考Senparc.Weixin.MP的JsApiTicketContainer)。

  ComponentContainer.cs代碼如下:

  1 /*----------------------------------------------------------------
  2     Copyright (C) 2015 Senparc
  3     
  4     文件名:ComponentContainer.cs
  5     文件功能描述:通用接口ComponentAccessToken容器,用於自動管理ComponentAccessToken,如果過期會重新獲取
  6     
  7     
  8     創建標識:Senparc - 20150430
  9 
 10     修改標識:Senparc - 20151004
 11     修改描述:v1.4.1 改名為ComponentContainer.cs,合並多個ComponentApp相關容器
 12 
 13 ----------------------------------------------------------------*/
 14 
 15 using System;
 16 using Senparc.Weixin.Containers;
 17 using Senparc.Weixin.Exceptions;
 18 using Senparc.Weixin.Open.Entities;
 19 using Senparc.Weixin.Open.Exceptions;
 20 
 21 namespace Senparc.Weixin.Open.CommonAPIs
 22 {
 23     /// <summary>
 24     /// 第三方APP信息包
 25     /// </summary>
 26     public class ComponentBag : BaseContainerBag
 27     {
 28         /// <summary>
 29         /// 第三方平台AppId
 30         /// </summary>
 31         public string ComponentAppId { get; set; }
 32         /// <summary>
 33         /// 第三方平台AppSecret
 34         /// </summary>
 35         public string ComponentAppSecret { get; set; }
 36 
 37         /// <summary>
 38         /// 第三方平台ComponentVerifyTicket(每隔10分鍾微信會主動推送到服務器,IP必須在白名單內)
 39         /// </summary>
 40         public string ComponentVerifyTicket { get; set; }
 41 
 42         /// <summary>
 43         /// ComponentAccessTokenResult
 44         /// </summary>
 45         public ComponentAccessTokenResult ComponentAccessTokenResult { get; set; }
 46         /// <summary>
 47         /// ComponentAccessToken過期時間
 48         /// </summary>
 49         public DateTime ComponentAccessTokenExpireTime { get; set; }
 50 
 51 
 52         /// <summary>
 53         /// PreAuthCodeResult 預授權碼結果
 54         /// </summary>
 55         public PreAuthCodeResult PreAuthCodeResult { get; set; }
 56         /// <summary>
 57         /// 預授權碼過期時間
 58         /// </summary>
 59         public DateTime PreAuthCodeExpireTime { get; set; }
 60 
 61 
 62         public string AuthorizerAccessToken { get; set; }
 63 
 64         /// <summary>
 65         /// 只針對這個AppId的鎖
 66         /// </summary>
 67         public object Lock = new object();
 68 
 69         /// <summary>
 70         /// ComponentBag
 71         /// </summary>
 72         public ComponentBag()
 73         {
 74             ComponentAccessTokenResult = new ComponentAccessTokenResult();
 75             ComponentAccessTokenExpireTime = DateTime.MinValue;
 76 
 77             PreAuthCodeResult = new PreAuthCodeResult();
 78             PreAuthCodeExpireTime = DateTime.MaxValue;
 79         }
 80     }
 81 
 82     /// <summary>
 83     /// 通用接口ComponentAccessToken容器,用於自動管理ComponentAccessToken,如果過期會重新獲取
 84     /// </summary>
 85     public class ComponentContainer : BaseContainer<ComponentBag>
 86     {
 87         private const string UN_REGISTER_ALERT = "此appId尚未注冊,ComponentContainer.Register完成注冊(全局執行一次即可)!";
 88 
 89         /// <summary>
 90         /// 檢查AppId是否已經注冊,如果沒有,則創建
 91         /// </summary>
 92         /// <param name="componentAppId"></param>
 93         /// <param name="componentAppSecret"></param>
 94         /// <param name="getNewToken"></param>
 95         private static void TryRegister(string componentAppId, string componentAppSecret, bool getNewToken = false)
 96         {
 97             if (!CheckRegistered(componentAppId) || getNewToken)
 98             {
 99                 Register(componentAppId, componentAppSecret, null);
100             }
101         }
102 
103         /// <summary>
104         /// 獲取ComponentVerifyTicket的方法
105         /// </summary>
106         public static Func<string> GetComponentVerifyTicketFunc = null;
107 
108         /// <summary>
109         /// 注冊應用憑證信息,此操作只是注冊,不會馬上獲取Token,並將清空之前的Token,
110         /// </summary>
111         /// <param name="componentAppId"></param>
112         /// <param name="componentAppSecret"></param>
113         /// <param name="getComponentVerifyTicketFunc">獲取ComponentVerifyTicket的方法</param>
114         public static void Register(string componentAppId, string componentAppSecret, Func<string> getComponentVerifyTicketFunc)
115         {
116             if (GetComponentVerifyTicketFunc == null)
117             {
118                 GetComponentVerifyTicketFunc = getComponentVerifyTicketFunc;
119             }
120 
121             Update(componentAppId, new ComponentBag()
122             {
123                 ComponentAppId = componentAppId,
124                 ComponentAppSecret = componentAppSecret,
125             });
126         }
127 
128         /// <summary>
129         /// 檢查是否已經注冊
130         /// </summary>
131         /// <param name="componentAppId"></param>
132         /// <returns></returns>
133         public static bool CheckRegistered(string componentAppId)
134         {
135             return ItemCollection.ContainsKey(componentAppId);
136         }
137 
138 
139         #region component_verify_ticket
140 
141         /// <summary>
142         /// 獲取ComponentVerifyTicket
143         /// </summary>
144         /// <param name="componentAppId"></param>
145         /// <returns>如果不存在,則返回null</returns>
146         public static string TryGetComponentVerifyTicket(string componentAppId)
147         {
148             if (!CheckRegistered(componentAppId))
149             {
150                 throw new WeixinOpenException(UN_REGISTER_ALERT);
151             }
152 
153             var componentVerifyTicket = TryGetItem(componentAppId, bag => bag.ComponentVerifyTicket);
154             if (componentVerifyTicket == default(string))
155             {
156                 if (GetComponentVerifyTicketFunc == null)
157                 {
158                     throw new WeixinOpenException("GetComponentVerifyTicketFunc必須在注冊時提供!", TryGetItem(componentAppId));
159                 }
160                 componentVerifyTicket = GetComponentVerifyTicketFunc(); //獲取最新的componentVerifyTicket
161             }
162             return componentVerifyTicket;
163         }
164 
165         /// <summary>
166         /// 更新ComponentVerifyTicket信息
167         /// </summary>
168         /// <param name="componentAppId"></param>
169         /// <param name="componentVerifyTicket"></param>
170         public static void UpdateComponentVerifyTicket(string componentAppId, string componentVerifyTicket)
171         {
172             Update(componentAppId, bag =>
173             {
174                 bag.ComponentVerifyTicket = componentVerifyTicket;
175             });
176         }
177 
178         #endregion
179 
180         #region component_access_token
181 
182         /// <summary>
183         /// 使用完整的應用憑證獲取Token,如果不存在將自動注冊
184         /// </summary>
185         /// <param name="componentAppId"></param>
186         /// <param name="componentAppSecret"></param>
187         /// <param name="getNewToken"></param>
188         /// <returns></returns>
189         public static string TryGetComponentAccessToken(string componentAppId, string componentAppSecret, bool getNewToken = false)
190         {
191             TryRegister(componentAppId, componentAppSecret, getNewToken);
192             return GetComponentAccessToken(componentAppId);
193         }
194 
195         /// <summary>
196         /// 獲取可用AccessToken
197         /// </summary>
198         /// <param name="componentAppId"></param>
199         /// <param name="getNewToken">是否強制重新獲取新的Token</param>
200         /// <returns></returns>
201         public static string GetComponentAccessToken(string componentAppId, bool getNewToken = false)
202         {
203             return GetComponentAccessTokenResult(componentAppId, getNewToken).component_access_token;
204         }
205 
206         /// <summary>
207         /// 獲取可用AccessToken
208         /// </summary>
209         /// <param name="componentAppId"></param>
210         /// <param name="getNewToken">是否強制重新獲取新的Token</param>
211         /// <returns></returns>
212         public static ComponentAccessTokenResult GetComponentAccessTokenResult(string componentAppId, bool getNewToken = false)
213         {
214             if (!CheckRegistered(componentAppId))
215             {
216                 throw new WeixinOpenException(UN_REGISTER_ALERT);
217             }
218 
219             var accessTokenBag = ItemCollection[componentAppId];
220             lock (accessTokenBag.Lock)
221             {
222                 if (getNewToken || accessTokenBag.ComponentAccessTokenExpireTime <= DateTime.Now)
223                 {
224                     //已過期,重新獲取
225                     var componentVerifyTicket = TryGetComponentVerifyTicket(componentAppId);
226 
227                     accessTokenBag.ComponentAccessTokenResult = CommonApi.GetComponentAccessToken(accessTokenBag.ComponentAppId, accessTokenBag.ComponentAppSecret, componentVerifyTicket);
228 
229                     accessTokenBag.ComponentAccessTokenExpireTime = DateTime.Now.AddSeconds(accessTokenBag.ComponentAccessTokenResult.expires_in);
230                 }
231             }
232             return accessTokenBag.ComponentAccessTokenResult;
233         }
234         #endregion
235 
236         #region pre_auth_code
237 
238         /// <summary>
239         /// 使用完整的應用憑證獲取Token,如果不存在將自動注冊
240         /// </summary>
241         /// <param name="componentAppId"></param>
242         /// <param name="componentAppSecret"></param>
243         /// <param name="componentVerifyTicket"></param>
244         /// <param name="getNewToken"></param>
245         /// <returns></returns>
246         public static string TryGetPreAuthCode(string componentAppId, string componentAppSecret, string componentVerifyTicket, bool getNewToken = false)
247         {
248             TryRegister(componentAppId, componentAppSecret, getNewToken);
249             return GetGetPreAuthCode(componentAppId);
250         }
251 
252         /// <summary>
253         /// 獲取可用Token
254         /// </summary>
255         /// <param name="componentAppId"></param>
256         /// <param name="getNewToken">是否強制重新獲取新的Token</param>
257         /// <returns></returns>
258         public static string GetGetPreAuthCode(string componentAppId, bool getNewToken = false)
259         {
260             return GetPreAuthCodeResult(componentAppId, getNewToken).pre_auth_code;
261         }
262 
263         /// <summary>
264         /// 獲取可用Token
265         /// </summary>
266         /// <param name="componentAppId"></param>
267         /// <param name="getNewToken">是否強制重新獲取新的Token</param>
268         /// <returns></returns>
269         public static PreAuthCodeResult GetPreAuthCodeResult(string componentAppId, bool getNewToken = false)
270         {
271             if (!CheckRegistered(componentAppId))
272             {
273                 throw new WeixinOpenException(UN_REGISTER_ALERT);
274             }
275 
276             var accessTokenBag = ItemCollection[componentAppId];
277             lock (accessTokenBag.Lock)
278             {
279                 if (getNewToken || accessTokenBag.PreAuthCodeExpireTime <= DateTime.Now)
280                 {
281                     //已過期,重新獲取
282                     var componentVerifyTicket = TryGetComponentVerifyTicket(componentAppId);
283 
284                     accessTokenBag.PreAuthCodeResult = CommonApi.GetPreAuthCode(accessTokenBag.ComponentAppId, accessTokenBag.ComponentAppSecret, componentVerifyTicket);
285 
286                     accessTokenBag.PreAuthCodeExpireTime = DateTime.Now.AddSeconds(accessTokenBag.PreAuthCodeResult.expires_in);
287                 }
288             }
289             return accessTokenBag.PreAuthCodeResult;
290         }
291         #endregion
292 
293     }
294 }
View Code

 

  在第一次使用ComponentContainer之前,和諸如AccessTokenContainer一樣,需要進行一次全局的注冊:

//全局只需注冊一次
ComponentContainer.Register(componentAppId, componentAppSecret, 
    _componentAppId =>{
        //獲取ComponentVerifyTicket的方法,通常是從數據庫或者日志文件中獲取
    });

  注意在上面的方法中,最后一個參數是getComponentVerifyTicketFunc,里面需要寫入用於獲取ComponentVerifyTicket的方法,通常是從數據庫或者日志文件中獲取(微信每10分鍾會推送一次最新的ComponentVerifyTicket到第三方服務器)。

   下面是一種可能的實現:

 1             //注冊第三方平台
 2             //定義ComponentVerifyTicketFunc
 3             Func<string, string> getComponentVerifyTicketFunc = componentAppId =>
 4              {
 5                  var file = Core.Utility.Server.GetMapPath(
 6                          "~/App_Data/OpenTicket/ComponentVerifyTicket/{0}.txt".With(componentAppId));
 7                  using (var fs = new FileStream(file, FileMode.Open))
 8                  {
 9                      using (var sr = new StreamReader(fs))
10                      {
11                          var ticket = sr.ReadToEnd();
12                          return ticket;
13                      }
14                  }
15              };
16             //執行注冊
17             ComponentContainer.Register(
18                 ConfigurationManager.AppSettings["Component_Appid"],
19                 ConfigurationManager.AppSettings["Component_Secret"], getComponentVerifyTicketFunc);

 

  2、創建AuthorizerContainer

  AuthorizerContainer負責儲存單個授權方(微信公眾號)所有相關的信息,包括:AuthorizerInfoResultJsApiTicketResult

  其中JsApiTicketResult相關的對象由原來的JsApiTicketContainer合並而來,JsApiTicketContainer現已刪除(升級方法參考Senparc.Weixin.MP的JsApiTicketContainer)。

  經常可能需要用到的AuthorizerInfoAuthorizerInfo_AuthorizationInfo都在AuthorizerInfoResult對象內。

  注意:AuthorizerContainer不需要外部執行Register()方法。因為它是依賴於ComponentContainer的,所以在執行AuthorizerContainer相關方法之前,請確保已經對ComponentContainer進行注冊。

 

  3、增加WeixinOpenException異常類型,用於捕捉更加精確的Senaprc.Weixin.Open相關的異常。

 1 /*----------------------------------------------------------------
 2     Copyright (C) 2015 Senparc
 3     
 4     文件名:WeixinOpenException.cs
 5     文件功能描述:微信開放平台異常處理類
 6     
 7     
 8     創建標識:Senparc - 20151004
 9 
10 ----------------------------------------------------------------*/
11 
12 using System;
13 using Senparc.Weixin.Exceptions;
14 using Senparc.Weixin.Open.CommonAPIs;
15 
16 namespace Senparc.Weixin.Open.Exceptions
17 {
18     public class WeixinOpenException : WeixinException
19     {
20         public ComponentBag ComponentBag { get; set; }
21 
22         public WeixinOpenException(string message, ComponentBag componentBag = null, Exception inner=null)
23             : base(message, inner)
24         {
25             ComponentBag = ComponentBag;
26         }
27     }
28 }
View Code

  

 

Senparc.Weixin.QY.dll

  升級內容(尚未正式發布):

  1、重構Container。

  2、完善接口。

 

 

  整個升級過程盡量確保了最小幅度的改動,之前版本的其他接口功能都不受影響,更多教程見:

  系列教程索引:http://www.cnblogs.com/szw/archive/2013/05/14/weixin-course-index.html

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM