diff --git a/src/Rabbit.WeiXin/Handlers/HandlerMiddleware.cs b/src/Rabbit.WeiXin/Handlers/HandlerMiddleware.cs
index ba56a5b..ee3b6ba 100644
--- a/src/Rabbit.WeiXin/Handlers/HandlerMiddleware.cs
+++ b/src/Rabbit.WeiXin/Handlers/HandlerMiddleware.cs
@@ -1,4 +1,5 @@
-using System.Threading.Tasks;
+using System.Collections.Generic;
+using System.Threading.Tasks;
namespace Rabbit.WeiXin.Handlers
{
@@ -13,14 +14,14 @@ public abstract class HandlerMiddleware
/// 下一个处理中间件。
protected HandlerMiddleware(HandlerMiddleware next)
{
- Next = next;
+ Next = next;
}
///
/// 下一个处理中间件。
///
protected HandlerMiddleware Next { get; private set; }
-
+
///
/// 调用。
///
diff --git a/src/Rabbit.WeiXin/MP/Api/AccountModel.cs b/src/Rabbit.WeiXin/MP/Api/AccountModel.cs
index c96ea81..f4bb53b 100644
--- a/src/Rabbit.WeiXin/MP/Api/AccountModel.cs
+++ b/src/Rabbit.WeiXin/MP/Api/AccountModel.cs
@@ -21,5 +21,10 @@ public class AccountModel
/// 获取调用接口凭证。
///
public Func GetAccessToken { get; set; }
+
+ ///
+ /// 获取Js调用接口凭证。
+ ///
+ public Func GetJsApiTicket { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Rabbit.WeiXin/MP/Api/CommonService.cs b/src/Rabbit.WeiXin/MP/Api/CommonService.cs
index 839e028..1cbf17b 100644
--- a/src/Rabbit.WeiXin/MP/Api/CommonService.cs
+++ b/src/Rabbit.WeiXin/MP/Api/CommonService.cs
@@ -19,6 +19,13 @@ public interface ICommonService
/// 第三方用户唯一凭证密钥,即appsecret。
AccessTokenModel GetAccessToken(bool ignoreCached = false);
+ ///
+ /// 获取JsApi的全局唯一票据。
+ ///
+ /// 是否忽略缓存。
+ /// 第三方用户唯一凭证密钥。
+ JsApiTicketModel GetJsApiTicket(bool ignoreCached = false);
+
///
/// 将一个url地址转换成一个短地址。
///
@@ -44,6 +51,9 @@ public sealed class CommonService : ICommonService
private static readonly ConcurrentDictionary Dictionary = new ConcurrentDictionary();
private readonly Lazy _accessTokenLazy;
+ private static readonly ConcurrentDictionary JsDictionary = new ConcurrentDictionary();
+ private readonly Lazy _jsApiTicketLazy;
+
#endregion Field
#region Constructor
@@ -56,6 +66,7 @@ public CommonService(AccountModel accountModel)
{
_accountModel = accountModel;
_accessTokenLazy = new Lazy(InternalGetAccessToken);
+ _jsApiTicketLazy = new Lazy(InternalGetJsApiTicket);
}
#endregion Constructor
@@ -79,6 +90,23 @@ public AccessTokenModel GetAccessToken(bool ignoreCached = false)
});
}
+ ///
+ /// 获取JsApi的全局唯一票据。
+ ///
+ /// 是否忽略缓存。
+ /// 第三方用户唯一凭证密钥。
+ public JsApiTicketModel GetJsApiTicket(bool ignoreCached = false)
+ {
+ var appId = _accountModel.AppId;
+ return JsDictionary.AddOrUpdate(appId, key => _jsApiTicketLazy.Value, (k, model) =>
+ {
+ //无效、过期、忽略缓存则重新获取。
+ if (model == null || model.IsExpired() || ignoreCached)
+ return InternalGetJsApiTicket();
+ return model;
+ });
+ }
+
///
/// 将一个url地址转换成一个短地址。
///
@@ -127,6 +155,13 @@ private AccessTokenModel InternalGetAccessToken()
return WeiXinHttpHelper.GetResultByJson(url);
}
+ private JsApiTicketModel InternalGetJsApiTicket()
+ {
+ var accessToken = _accountModel.GetAccessToken();
+ var url = string.Format("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={0}&type=jsapi", accessToken);
+ return WeiXinHttpHelper.GetResultByJson(url);
+ }
+
#endregion Private Method
}
@@ -177,5 +212,50 @@ public bool IsExpired()
}
}
+ ///
+ /// 获取JsApi全局唯一票据结果模型
+ ///
+ public sealed class JsApiTicketModel
+ {
+ #region Constructor
+
+ ///
+ /// 初始化一个新的JsApi的全局唯一票据。
+ ///
+ public JsApiTicketModel()
+ {
+ CreateTime = DateTime.Now;
+ }
+
+ #endregion Constructor
+
+ ///
+ /// 全局唯一票据
+ ///
+ [JsonProperty("ticket")]
+ public string Ticket { get; set; }
+
+ ///
+ /// 凭证有效时间(秒)。
+ ///
+ [JsonProperty("expires_in")]
+ public int ExpiresIn { get; set; }
+
+ ///
+ /// 创建时间。
+ ///
+ [JsonIgnore]
+ public DateTime CreateTime { get; private set; }
+
+ ///
+ /// 是否过期。
+ ///
+ /// 如果过期返回true,否则返回false。
+ public bool IsExpired()
+ {
+ return CreateTime.AddSeconds(ExpiresIn - 20/*不采用最后的期限作为判断,防止在很少的时间内到期导致后续的逻辑无法执行*/) <= DateTime.Now;
+ }
+ }
+
#endregion Help Class
}
\ No newline at end of file
diff --git a/src/Rabbit.WeiXin/MP/Api/JsSdk/JsConfigService.cs b/src/Rabbit.WeiXin/MP/Api/JsSdk/JsConfigService.cs
new file mode 100644
index 0000000..636da82
--- /dev/null
+++ b/src/Rabbit.WeiXin/MP/Api/JsSdk/JsConfigService.cs
@@ -0,0 +1,84 @@
+using Newtonsoft.Json;
+using Rabbit.WeiXin.Utility;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Rabbit.WeiXin.Utility.Extensions;
+
+namespace Rabbit.WeiXin.MP.Api.Js
+{
+
+ ///
+ /// Js配置生成生成服务
+ ///
+ public interface IJsConfigService
+ {
+ ///
+ /// 生成微信JsSDK配置信息
+ ///
+ ///
+ ///
+ JsSdkConfigModel Create(string currentUrl);
+ }
+
+
+ ///
+ /// Js配置生成生成服务
+ ///
+ public class JsConfigService : IJsConfigService
+ {
+ private readonly AccountModel _accountModel;
+
+ ///
+ /// ctor
+ ///
+ ///
+ public JsConfigService(AccountModel accountModel)
+ {
+ _accountModel = accountModel;
+ }
+
+ public JsSdkConfigModel Create(string currentUrl)
+ {
+ JsSdkConfigModel result = new JsSdkConfigModel();
+ result.Timestamp = DateTimeHelper.GetTimeStampByTime(DateTime.Now).ToString();
+ result.AppId = _accountModel.AppId;
+ result.NonceStr = StringHelper.GetRandomString(32);
+
+
+ return result;
+ }
+ }
+
+
+ ///
+ ///
+ ///
+ public class JsSdkConfigModel
+ {
+ ///
+ /// AppId
+ ///
+ [JsonProperty("appId")]
+ public string AppId { get; set; }
+
+ ///
+ /// 时间戳
+ ///
+ [JsonProperty("timestamp")]
+ public string Timestamp { get; set; }
+
+ ///
+ /// 随机字符串
+ ///
+ [JsonProperty("nonceStr")]
+ public string NonceStr { get; set; }
+
+ ///
+ /// Signature
+ ///
+ [JsonProperty("signature")]
+ public string Signature { get; set; }
+ }
+}
diff --git a/src/Rabbit.WeiXin/Utility/Extensions/SerializeExtensions.cs b/src/Rabbit.WeiXin/Utility/Extensions/SerializeExtensions.cs
new file mode 100644
index 0000000..c74843f
--- /dev/null
+++ b/src/Rabbit.WeiXin/Utility/Extensions/SerializeExtensions.cs
@@ -0,0 +1,27 @@
+using Rabbit.WeiXin.MP.Messages;
+using Rabbit.WeiXin.MP.Messages.Response;
+using Rabbit.WeiXin.MP.Serialization;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Rabbit.WeiXin.Utility.Extensions
+{
+ ///
+ /// 序列化对象
+ ///
+ public static class SerializeExtensions
+ {
+ ///
+ /// 序列化Response消息
+ ///
+ /// Response消息
+ ///
+ public static string Serialize(this IResponseMessage responseMessage)
+ {
+ var facotry = new ResponseMessageFactory(new MessageFormatterFactory());
+ return facotry.GetXmlByReponseMessage(responseMessage);
+ }
+ }
+}
diff --git a/src/Rabbit.WeiXin/Utility/StringHelper.cs b/src/Rabbit.WeiXin/Utility/StringHelper.cs
new file mode 100644
index 0000000..11aa23c
--- /dev/null
+++ b/src/Rabbit.WeiXin/Utility/StringHelper.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Rabbit.WeiXin.Utility
+{
+
+ ///
+ /// 字符串帮助类
+ ///
+ public class StringHelper
+ {
+ static Random random = new Random();
+ static string[] arr = new string[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
+
+
+ ///
+ /// 获得随机长度字符串
+ ///
+ /// 字符串长度
+ ///
+ public static string GetRandomString(int length)
+ {
+ var result = new StringBuilder();
+ for (var i = 0; i < length; i++)
+ {
+ result.Append(arr[random.Next(37)]);
+ }
+ return result.ToString();
+ }
+ }
+}
diff --git a/test/Rabbit.WeiXin.Tests/ApiTestBase.cs b/test/Rabbit.WeiXin.Tests/ApiTestBase.cs
index 48a7a13..b0045a6 100644
--- a/test/Rabbit.WeiXin.Tests/ApiTestBase.cs
+++ b/test/Rabbit.WeiXin.Tests/ApiTestBase.cs
@@ -29,7 +29,8 @@ public ApiTestBase()
{
AppId = AppId,
AppSecret = AppSecret,
- GetAccessToken = GetAccessToken
+ GetAccessToken = GetAccessToken,
+ GetJsApiTicket = GetJsApiTicket,
};
CommonService = new CommonService(AccountModel);
}
@@ -43,6 +44,11 @@ protected string GetAccessToken()
return CommonService.GetAccessToken().AccessToken;
}
+ protected string GetJsApiTicket()
+ {
+ return CommonService.GetJsApiTicket().Ticket;
+ }
+
#endregion Protected Method
}
}
\ No newline at end of file
diff --git a/test/Rabbit.WeiXin.Tests/CommonServiceTest.cs b/test/Rabbit.WeiXin.Tests/CommonServiceTest.cs
index a44556f..9be3ba8 100644
--- a/test/Rabbit.WeiXin.Tests/CommonServiceTest.cs
+++ b/test/Rabbit.WeiXin.Tests/CommonServiceTest.cs
@@ -31,6 +31,23 @@ public void GetAccessTokenTest()
}
[Fact]
+ public void GetJsApiTicketTest()
+ {
+ Func get =
+ ignoreCached => CommonService.GetJsApiTicket(ignoreCached);
+
+ var model = get(false);
+
+ Assert.NotNull(model.Ticket);
+
+ Assert.False(model.IsExpired());
+
+ var model2 = get(false);
+ Assert.Equal(model.Ticket, model2.Ticket);
+ Assert.False(model2.IsExpired());
+ }
+
+ //[Fact]
public void GenerateShotAddressTest()
{
const string url = "http://cn.bing.com/search?q=windows10&go=%E6%8F%90%E4%BA%A4&qs=n&form=QBLH&pq=windows10&sc=8-9&sp=-1&sk=&ghc=1&cvid=6d333afcfbec4834bbf3ce592b699f66";