【重要更新】Senparc.Weixin SDK v4.3.3升级说明

时间:2022-03-07 08:45:11

  为了更好地适应微信越来越快的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的属性,提供了解决方案。

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

【重要更新】Senparc.Weixin SDK v4.3.3升级说明

  其中: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 > )
{
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内部部分接口已经在使用,如创建卡券的接口:

         /// <summary>
/// 创建卡券
/// </summary>
/// <param name="accessTokenOrAppId"></param>
/// <param name="cardInfo">创建卡券需要的数据,格式可以看CardCreateData.cs</param>
/// <param name="timeOut">代理请求超时时间(毫秒)</param>
/// <returns></returns>
public static CardCreateResultJson CreateCard(string accessTokenOrAppId, BaseCardInfo cardInfo, int timeOut = Config.TIME_OUT)
{
return ApiHandlerWapper.TryCommonApi(accessToken =>
{
var urlFormat = string.Format("https://api.weixin.qq.com/card/create?access_token={0}", accessToken); CardCreateInfo cardData = null;
CardType cardType = cardInfo.GetCardType(); switch (cardType)
{
...
} var jsonSetting = new JsonSetting(true, null,
new List<Type>()
{
//typeof (Modify_Msg_Operation),
//typeof (CardCreateInfo),
typeof (Card_BaseInfoBase)//过滤Modify_Msg_Operation主要起作用的是这个
}); var result = CommonJsonSend.Send<CardCreateResultJson>(null, urlFormat, cardData, timeOut: timeOut,
//针对特殊字段的null值进行过滤
jsonSetting: jsonSetting);
return result; }, accessTokenOrAppId);
}

  第一步:定义JsonSetting,确定需要过滤的规则。

  第二步:将JsonSetting对象传入CommonJsonSend.Send()方法。

  2、增加Containers基础功能:用于作为其他子库中所有Container(如AccessTokenContainer)的基类,并提供配套的ContainerBag(信息包)基类。

  如下图所示:

【重要更新】Senparc.Weixin SDK v4.3.3升级说明

  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进行替换:

【重要更新】Senparc.Weixin SDK v4.3.3升级说明

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

【重要更新】Senparc.Weixin SDK v4.3.3升级说明

  对于JsApiTicketContainer相关的替换也参考以上步骤:

【重要更新】Senparc.Weixin SDK v4.3.3升级说明

  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代码如下:

 /*----------------------------------------------------------------
Copyright (C) 2015 Senparc 文件名:ComponentContainer.cs
文件功能描述:通用接口ComponentAccessToken容器,用于自动管理ComponentAccessToken,如果过期会重新获取 创建标识:Senparc - 20150430 修改标识:Senparc - 20151004
修改描述:v1.4.1 改名为ComponentContainer.cs,合并多个ComponentApp相关容器 ----------------------------------------------------------------*/ using System;
using Senparc.Weixin.Containers;
using Senparc.Weixin.Exceptions;
using Senparc.Weixin.Open.Entities;
using Senparc.Weixin.Open.Exceptions; namespace Senparc.Weixin.Open.CommonAPIs
{
/// <summary>
/// 第三方APP信息包
/// </summary>
public class ComponentBag : BaseContainerBag
{
/// <summary>
/// 第三方平台AppId
/// </summary>
public string ComponentAppId { get; set; }
/// <summary>
/// 第三方平台AppSecret
/// </summary>
public string ComponentAppSecret { get; set; } /// <summary>
/// 第三方平台ComponentVerifyTicket(每隔10分钟微信会主动推送到服务器,IP必须在白名单内)
/// </summary>
public string ComponentVerifyTicket { get; set; } /// <summary>
/// ComponentAccessTokenResult
/// </summary>
public ComponentAccessTokenResult ComponentAccessTokenResult { get; set; }
/// <summary>
/// ComponentAccessToken过期时间
/// </summary>
public DateTime ComponentAccessTokenExpireTime { get; set; } /// <summary>
/// PreAuthCodeResult 预授权码结果
/// </summary>
public PreAuthCodeResult PreAuthCodeResult { get; set; }
/// <summary>
/// 预授权码过期时间
/// </summary>
public DateTime PreAuthCodeExpireTime { get; set; } public string AuthorizerAccessToken { get; set; } /// <summary>
/// 只针对这个AppId的锁
/// </summary>
public object Lock = new object(); /// <summary>
/// ComponentBag
/// </summary>
public ComponentBag()
{
ComponentAccessTokenResult = new ComponentAccessTokenResult();
ComponentAccessTokenExpireTime = DateTime.MinValue; PreAuthCodeResult = new PreAuthCodeResult();
PreAuthCodeExpireTime = DateTime.MaxValue;
}
} /// <summary>
/// 通用接口ComponentAccessToken容器,用于自动管理ComponentAccessToken,如果过期会重新获取
/// </summary>
public class ComponentContainer : BaseContainer<ComponentBag>
{
private const string UN_REGISTER_ALERT = "此appId尚未注册,ComponentContainer.Register完成注册(全局执行一次即可)!"; /// <summary>
/// 检查AppId是否已经注册,如果没有,则创建
/// </summary>
/// <param name="componentAppId"></param>
/// <param name="componentAppSecret"></param>
/// <param name="getNewToken"></param>
private static void TryRegister(string componentAppId, string componentAppSecret, bool getNewToken = false)
{
if (!CheckRegistered(componentAppId) || getNewToken)
{
Register(componentAppId, componentAppSecret, null);
}
} /// <summary>
/// 获取ComponentVerifyTicket的方法
/// </summary>
public static Func<string> GetComponentVerifyTicketFunc = null; /// <summary>
/// 注册应用凭证信息,此操作只是注册,不会马上获取Token,并将清空之前的Token,
/// </summary>
/// <param name="componentAppId"></param>
/// <param name="componentAppSecret"></param>
/// <param name="getComponentVerifyTicketFunc">获取ComponentVerifyTicket的方法</param>
public static void Register(string componentAppId, string componentAppSecret, Func<string> getComponentVerifyTicketFunc)
{
if (GetComponentVerifyTicketFunc == null)
{
GetComponentVerifyTicketFunc = getComponentVerifyTicketFunc;
} Update(componentAppId, new ComponentBag()
{
ComponentAppId = componentAppId,
ComponentAppSecret = componentAppSecret,
});
} /// <summary>
/// 检查是否已经注册
/// </summary>
/// <param name="componentAppId"></param>
/// <returns></returns>
public static bool CheckRegistered(string componentAppId)
{
return ItemCollection.ContainsKey(componentAppId);
} #region component_verify_ticket /// <summary>
/// 获取ComponentVerifyTicket
/// </summary>
/// <param name="componentAppId"></param>
/// <returns>如果不存在,则返回null</returns>
public static string TryGetComponentVerifyTicket(string componentAppId)
{
if (!CheckRegistered(componentAppId))
{
throw new WeixinOpenException(UN_REGISTER_ALERT);
} var componentVerifyTicket = TryGetItem(componentAppId, bag => bag.ComponentVerifyTicket);
if (componentVerifyTicket == default(string))
{
if (GetComponentVerifyTicketFunc == null)
{
throw new WeixinOpenException("GetComponentVerifyTicketFunc必须在注册时提供!", TryGetItem(componentAppId));
}
componentVerifyTicket = GetComponentVerifyTicketFunc(); //获取最新的componentVerifyTicket
}
return componentVerifyTicket;
} /// <summary>
/// 更新ComponentVerifyTicket信息
/// </summary>
/// <param name="componentAppId"></param>
/// <param name="componentVerifyTicket"></param>
public static void UpdateComponentVerifyTicket(string componentAppId, string componentVerifyTicket)
{
Update(componentAppId, bag =>
{
bag.ComponentVerifyTicket = componentVerifyTicket;
});
} #endregion #region component_access_token /// <summary>
/// 使用完整的应用凭证获取Token,如果不存在将自动注册
/// </summary>
/// <param name="componentAppId"></param>
/// <param name="componentAppSecret"></param>
/// <param name="getNewToken"></param>
/// <returns></returns>
public static string TryGetComponentAccessToken(string componentAppId, string componentAppSecret, bool getNewToken = false)
{
TryRegister(componentAppId, componentAppSecret, getNewToken);
return GetComponentAccessToken(componentAppId);
} /// <summary>
/// 获取可用AccessToken
/// </summary>
/// <param name="componentAppId"></param>
/// <param name="getNewToken">是否强制重新获取新的Token</param>
/// <returns></returns>
public static string GetComponentAccessToken(string componentAppId, bool getNewToken = false)
{
return GetComponentAccessTokenResult(componentAppId, getNewToken).component_access_token;
} /// <summary>
/// 获取可用AccessToken
/// </summary>
/// <param name="componentAppId"></param>
/// <param name="getNewToken">是否强制重新获取新的Token</param>
/// <returns></returns>
public static ComponentAccessTokenResult GetComponentAccessTokenResult(string componentAppId, bool getNewToken = false)
{
if (!CheckRegistered(componentAppId))
{
throw new WeixinOpenException(UN_REGISTER_ALERT);
} var accessTokenBag = ItemCollection[componentAppId];
lock (accessTokenBag.Lock)
{
if (getNewToken || accessTokenBag.ComponentAccessTokenExpireTime <= DateTime.Now)
{
//已过期,重新获取
var componentVerifyTicket = TryGetComponentVerifyTicket(componentAppId); accessTokenBag.ComponentAccessTokenResult = CommonApi.GetComponentAccessToken(accessTokenBag.ComponentAppId, accessTokenBag.ComponentAppSecret, componentVerifyTicket); accessTokenBag.ComponentAccessTokenExpireTime = DateTime.Now.AddSeconds(accessTokenBag.ComponentAccessTokenResult.expires_in);
}
}
return accessTokenBag.ComponentAccessTokenResult;
}
#endregion #region pre_auth_code /// <summary>
/// 使用完整的应用凭证获取Token,如果不存在将自动注册
/// </summary>
/// <param name="componentAppId"></param>
/// <param name="componentAppSecret"></param>
/// <param name="componentVerifyTicket"></param>
/// <param name="getNewToken"></param>
/// <returns></returns>
public static string TryGetPreAuthCode(string componentAppId, string componentAppSecret, string componentVerifyTicket, bool getNewToken = false)
{
TryRegister(componentAppId, componentAppSecret, getNewToken);
return GetGetPreAuthCode(componentAppId);
} /// <summary>
/// 获取可用Token
/// </summary>
/// <param name="componentAppId"></param>
/// <param name="getNewToken">是否强制重新获取新的Token</param>
/// <returns></returns>
public static string GetGetPreAuthCode(string componentAppId, bool getNewToken = false)
{
return GetPreAuthCodeResult(componentAppId, getNewToken).pre_auth_code;
} /// <summary>
/// 获取可用Token
/// </summary>
/// <param name="componentAppId"></param>
/// <param name="getNewToken">是否强制重新获取新的Token</param>
/// <returns></returns>
public static PreAuthCodeResult GetPreAuthCodeResult(string componentAppId, bool getNewToken = false)
{
if (!CheckRegistered(componentAppId))
{
throw new WeixinOpenException(UN_REGISTER_ALERT);
} var accessTokenBag = ItemCollection[componentAppId];
lock (accessTokenBag.Lock)
{
if (getNewToken || accessTokenBag.PreAuthCodeExpireTime <= DateTime.Now)
{
//已过期,重新获取
var componentVerifyTicket = TryGetComponentVerifyTicket(componentAppId); accessTokenBag.PreAuthCodeResult = CommonApi.GetPreAuthCode(accessTokenBag.ComponentAppId, accessTokenBag.ComponentAppSecret, componentVerifyTicket); accessTokenBag.PreAuthCodeExpireTime = DateTime.Now.AddSeconds(accessTokenBag.PreAuthCodeResult.expires_in);
}
}
return accessTokenBag.PreAuthCodeResult;
}
#endregion }
}

  在第一次使用ComponentContainer之前,和诸如AccessTokenContainer一样,需要进行一次全局的注册:

//全局只需注册一次
ComponentContainer.Register(componentAppId, componentAppSecret,
_componentAppId =>{
//获取ComponentVerifyTicket的方法,通常是从数据库或者日志文件中获取
});

  注意在上面的方法中,最后一个参数是getComponentVerifyTicketFunc,里面需要写入用于获取ComponentVerifyTicket的方法,通常是从数据库或者日志文件中获取(微信每10分钟会推送一次最新的ComponentVerifyTicket到第三方服务器)。

  下面是一种可能的实现:

             //注册第三方平台
//定义ComponentVerifyTicketFunc
Func<string, string> getComponentVerifyTicketFunc = componentAppId =>
{
var file = Core.Utility.Server.GetMapPath(
"~/App_Data/OpenTicket/ComponentVerifyTicket/{0}.txt".With(componentAppId));
using (var fs = new FileStream(file, FileMode.Open))
{
using (var sr = new StreamReader(fs))
{
var ticket = sr.ReadToEnd();
return ticket;
}
}
};
//执行注册
ComponentContainer.Register(
ConfigurationManager.AppSettings["Component_Appid"],
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相关的异常。

 /*----------------------------------------------------------------
Copyright (C) 2015 Senparc 文件名:WeixinOpenException.cs
文件功能描述:微信开放平台异常处理类 创建标识:Senparc - 20151004 ----------------------------------------------------------------*/ using System;
using Senparc.Weixin.Exceptions;
using Senparc.Weixin.Open.CommonAPIs; namespace Senparc.Weixin.Open.Exceptions
{
public class WeixinOpenException : WeixinException
{
public ComponentBag ComponentBag { get; set; } public WeixinOpenException(string message, ComponentBag componentBag = null, Exception inner=null)
: base(message, inner)
{
ComponentBag = ComponentBag;
}
}
}

Senparc.Weixin.QY.dll

  升级内容(尚未正式发布):

  1、重构Container。

  2、完善接口。

  整个升级过程尽量确保了最小幅度的改动,之前版本的其他接口功能都不受影响,更多教程见: