为了更好地适应微信越来越快的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) }); } } } }
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 } } }
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内部的方法名也进行了调整,具体如下:
# | 原方法名称 | 现方法名称 |
1 | AccessTokenContainer.Register() | 不变 |
2 | AccessTokenContainer.GetFirstOrDefaultAppId() | 不变 |
3 | AccessTokenContainer.TryGetToken() | AccessTokenContainer.TryGetAccessToken() |
4 | AccessTokenContainer.GetToken() | AccessTokenContainer.GetAccessToken() |
5 | AccessTokenContainer.GetTokenResult() | AccessTokenContainer.GetAccessTokenResult() |
6 | JsApiTicketContainer.TryGetTicket() | AccessTokenContainer.TryGetJsApiTicket() |
7 | 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提供了所有第三方平台需要用到的缓存信息以及获取方法,包括:ComponentVerifyTicket、PreAuthCode、AuthorizerAccessToken。
其中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 }
在第一次使用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负责储存单个授权方(微信公众号)所有相关的信息,包括:AuthorizerInfoResult、JsApiTicketResult。
其中JsApiTicketResult相关的对象由原来的JsApiTicketContainer合并而来,JsApiTicketContainer现已删除(升级方法参考Senparc.Weixin.MP的JsApiTicketContainer)。
经常可能需要用到的AuthorizerInfo和AuthorizerInfo_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 }
Senparc.Weixin.QY.dll
升级内容(尚未正式发布):
1、重构Container。
2、完善接口。
整个升级过程尽量确保了最小幅度的改动,之前版本的其他接口功能都不受影响,更多教程见:
QQ:498977166
http://szw.cnblogs.com/
研究、探讨.NET开发
转载请注明出处和作者,谢谢!
Senparc官方教程《微信开发深度解析:微信公众号、小程序高效开发秘籍》,耗时2年精心打造的微信开发权威教程,点击这里,购买正版!