• 【重要更新】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

     
  • 相关阅读:
    我的第二个裸板程序之链接地址与存储地址
    ARM你必须知道的事儿——为啥“PC = PC + 8”?
    typedef你真的理解么?
    centos使用--vsftpd配置
    centos使用--zsh
    laravel5.2总结--序列化
    laravel5.2总结--csrf保护
    laravel5.2总结--任务调度
    laravel5.2总结--本地化以及常量的使用
    laravel5.2总结--文件上传
  • 原文地址:https://www.cnblogs.com/szw/p/Senparc_Weixin-SDK-v4_3_3-update.html
Copyright © 2020-2023  润新知