Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/Rabbit.WeiXin/Handlers/HandlerMiddleware.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Rabbit.WeiXin.Handlers
{
Expand All @@ -13,14 +14,14 @@ public abstract class HandlerMiddleware
/// <param name="next">下一个处理中间件。</param>
protected HandlerMiddleware(HandlerMiddleware next)
{
Next = next;
Next = next;
}

/// <summary>
/// 下一个处理中间件。
/// </summary>
protected HandlerMiddleware Next { get; private set; }

/// <summary>
/// 调用。
/// </summary>
Expand Down
5 changes: 5 additions & 0 deletions src/Rabbit.WeiXin/MP/Api/AccountModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,10 @@ public class AccountModel
/// 获取调用接口凭证。
/// </summary>
public Func<string> GetAccessToken { get; set; }

/// <summary>
/// 获取Js调用接口凭证。
/// </summary>
public Func<string> GetJsApiTicket { get; set; }
}
}
80 changes: 80 additions & 0 deletions src/Rabbit.WeiXin/MP/Api/CommonService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ public interface ICommonService
/// <returns>第三方用户唯一凭证密钥,即appsecret。</returns>
AccessTokenModel GetAccessToken(bool ignoreCached = false);

/// <summary>
/// 获取JsApi的全局唯一票据。
/// </summary>
/// <param name="ignoreCached">是否忽略缓存。</param>
/// <returns>第三方用户唯一凭证密钥。</returns>
JsApiTicketModel GetJsApiTicket(bool ignoreCached = false);

/// <summary>
/// 将一个url地址转换成一个短地址。
/// </summary>
Expand All @@ -44,6 +51,9 @@ public sealed class CommonService : ICommonService
private static readonly ConcurrentDictionary<string, AccessTokenModel> Dictionary = new ConcurrentDictionary<string, AccessTokenModel>();
private readonly Lazy<AccessTokenModel> _accessTokenLazy;

private static readonly ConcurrentDictionary<string, JsApiTicketModel> JsDictionary = new ConcurrentDictionary<string, JsApiTicketModel>();
private readonly Lazy<JsApiTicketModel> _jsApiTicketLazy;

#endregion Field

#region Constructor
Expand All @@ -56,6 +66,7 @@ public CommonService(AccountModel accountModel)
{
_accountModel = accountModel;
_accessTokenLazy = new Lazy<AccessTokenModel>(InternalGetAccessToken);
_jsApiTicketLazy = new Lazy<JsApiTicketModel>(InternalGetJsApiTicket);
}

#endregion Constructor
Expand All @@ -79,6 +90,23 @@ public AccessTokenModel GetAccessToken(bool ignoreCached = false)
});
}

/// <summary>
/// 获取JsApi的全局唯一票据。
/// </summary>
/// <param name="ignoreCached">是否忽略缓存。</param>
/// <returns>第三方用户唯一凭证密钥。</returns>
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;
});
}

/// <summary>
/// 将一个url地址转换成一个短地址。
/// </summary>
Expand Down Expand Up @@ -127,6 +155,13 @@ private AccessTokenModel InternalGetAccessToken()
return WeiXinHttpHelper.GetResultByJson<AccessTokenModel>(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<JsApiTicketModel>(url);
}

#endregion Private Method
}

Expand Down Expand Up @@ -177,5 +212,50 @@ public bool IsExpired()
}
}

/// <summary>
/// 获取JsApi全局唯一票据结果模型
/// </summary>
public sealed class JsApiTicketModel
{
#region Constructor

/// <summary>
/// 初始化一个新的JsApi的全局唯一票据。
/// </summary>
public JsApiTicketModel()
{
CreateTime = DateTime.Now;
}

#endregion Constructor

/// <summary>
/// 全局唯一票据
/// </summary>
[JsonProperty("ticket")]
public string Ticket { get; set; }

/// <summary>
/// 凭证有效时间(秒)。
/// </summary>
[JsonProperty("expires_in")]
public int ExpiresIn { get; set; }

/// <summary>
/// 创建时间。
/// </summary>
[JsonIgnore]
public DateTime CreateTime { get; private set; }

/// <summary>
/// 是否过期。
/// </summary>
/// <returns>如果过期返回true,否则返回false。</returns>
public bool IsExpired()
{
return CreateTime.AddSeconds(ExpiresIn - 20/*不采用最后的期限作为判断,防止在很少的时间内到期导致后续的逻辑无法执行*/) <= DateTime.Now;
}
}

#endregion Help Class
}
84 changes: 84 additions & 0 deletions src/Rabbit.WeiXin/MP/Api/JsSdk/JsConfigService.cs
Original file line number Diff line number Diff line change
@@ -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
{

/// <summary>
/// Js配置生成生成服务
/// </summary>
public interface IJsConfigService
{
/// <summary>
/// 生成微信JsSDK配置信息
/// </summary>
/// <param name="currentUrl"></param>
/// <returns></returns>
JsSdkConfigModel Create(string currentUrl);
}


/// <summary>
/// Js配置生成生成服务
/// </summary>
public class JsConfigService : IJsConfigService
{
private readonly AccountModel _accountModel;

/// <summary>
/// ctor
/// </summary>
/// <param name="accountModel"></param>
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;
}
}


/// <summary>
///
/// </summary>
public class JsSdkConfigModel
{
/// <summary>
/// AppId
/// </summary>
[JsonProperty("appId")]
public string AppId { get; set; }

/// <summary>
/// 时间戳
/// </summary>
[JsonProperty("timestamp")]
public string Timestamp { get; set; }

/// <summary>
/// 随机字符串
/// </summary>
[JsonProperty("nonceStr")]
public string NonceStr { get; set; }

/// <summary>
/// Signature
/// </summary>
[JsonProperty("signature")]
public string Signature { get; set; }
}
}
27 changes: 27 additions & 0 deletions src/Rabbit.WeiXin/Utility/Extensions/SerializeExtensions.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// 序列化对象
/// </summary>
public static class SerializeExtensions
{
/// <summary>
/// 序列化Response消息
/// </summary>
/// <param name="responseMessage">Response消息</param>
/// <returns></returns>
public static string Serialize(this IResponseMessage responseMessage)
{
var facotry = new ResponseMessageFactory(new MessageFormatterFactory());
return facotry.GetXmlByReponseMessage(responseMessage);
}
}
}
33 changes: 33 additions & 0 deletions src/Rabbit.WeiXin/Utility/StringHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Rabbit.WeiXin.Utility
{

/// <summary>
/// 字符串帮助类
/// </summary>
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" };


/// <summary>
/// 获得随机长度字符串
/// </summary>
/// <param name="length">字符串长度</param>
/// <returns></returns>
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();
}
}
}
8 changes: 7 additions & 1 deletion test/Rabbit.WeiXin.Tests/ApiTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ public ApiTestBase()
{
AppId = AppId,
AppSecret = AppSecret,
GetAccessToken = GetAccessToken
GetAccessToken = GetAccessToken,
GetJsApiTicket = GetJsApiTicket,
};
CommonService = new CommonService(AccountModel);
}
Expand All @@ -43,6 +44,11 @@ protected string GetAccessToken()
return CommonService.GetAccessToken().AccessToken;
}

protected string GetJsApiTicket()
{
return CommonService.GetJsApiTicket().Ticket;
}

#endregion Protected Method
}
}
17 changes: 17 additions & 0 deletions test/Rabbit.WeiXin.Tests/CommonServiceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ public void GetAccessTokenTest()
}

[Fact]
public void GetJsApiTicketTest()
{
Func<bool, JsApiTicketModel> 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";
Expand Down