一.项目说明
- 后端使用Nuget包: skit.flurlhttpclient.wechat.api
- 前端使用vue3 + weixin-js-sdk,安装命令: npm install weixin-js-sdk
二.公众号配置
- 登录微信开放平台:https://mp.weixin.qq.com/
找到功能设置,配置Js接口安全域名
注意:此域名为前端访问地址使用的域名
2.配置白名单 用来获取微信 Access token
****注意:如果接口报错信息为ip不在白名单内,请检查服务器是否有两个ip地址,两个ip地址必须都要填入,否则接口无法获取Access token
三.前端实现
1.分享时需调用后端接口,返回微信 WXConfig 配置数据,appId,timestamp,nonceStr,signature
2.jsApiList,为调用微信接口方法名称,具体参考文档:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html
3.要分享的地址必须与微信打开页面的地址相同,否则无法带缩略图分享
const getWxShare = async () => {
var url = window.location.href;
let param = {
url: url
};
await GetWXConfig(param).then(res => {
if (res.data.code == 200 && res.data.data != null) {
//微信配置
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: res.data.data.appId, // 必填,公众号的唯一标识
timestamp: res.data.data.timestamp, // 必填,生成签名的时间戳
nonceStr: res.data.data.nonceStr, // 必填,生成签名的随机串
signature: res.data.data.signature, // 必填,签名
jsApiList: [
'updateAppMessageShareData',
'updateTimelineShareData'
] // 必填,需要使用的JS接口列表
});
// wx.error(function(res) {
// console.log(res)
// });
//转发信息
var wxtitle = "";
var wximg = "";
var wxdescription = "";
//config配置完成后可调用转发
wx.ready(function() { //需在用户可能点击分享按钮前就先调用
//自定义“分享给朋友”
wx.updateAppMessageShareData({
title: wxtitle, // 分享标题
desc: wxdescription, // 分享描述
link: url, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: wximg, // 分享图标
success: function() {
// 设置成功
}
})
//自定义“分享到朋友圈”
wx.updateTimelineShareData({
title: wxtitle, // 分享标题
link: url, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: wximg, // 分享图标
success: function() {
// 设置成功
}
})
});
}
})
}
四.后端实现
1.Nuget 引用skit.flurlhttpclient.wechat.api
2.接口实现
/// <summary>
/// 微信
/// </summary>
[Route("WeChatSerivce/api/[controller]")]
[ApiController]
public class WeChatController : ControllerBase
{
#region 缓存键:Token、Ticket
/// <summary>
/// Access token缓存键值
/// </summary>
public const string ResCgibinTokenKey = "ResCgibinTokenKey";
/// <summary>
/// Ticket缓存键值
/// </summary>
public const string TicketKey = "TicketKey";
#endregion
#region 依赖注入、初始化微信服务
private readonly IConfiguration _configuration;
private readonly WechatApiClient _wechatApiClient;
private readonly IMemoryCache _memoryCache;
/// <summary>
/// 依赖注入、初始化微信服务
/// </summary>
/// <param name="configuration">读取配置文件</param>
/// <param name="memoryCache">缓存</param>
public WeChatController(IConfiguration configuration, IMemoryCache memoryCache)
{
_configuration = configuration;
_memoryCache = memoryCache;
var appid = _configuration["Wechat:WechatAppId"];
var secret = _configuration["Wechat:WechatAppSecret"];
//初始化微信服务
WechatApiClientOptions clientOptions = new WechatApiClientOptions()
{
AppId = appid,
AppSecret = secret
};
_wechatApiClient = new WechatApiClient(clientOptions);
}
#endregion
#region 校验缓存Token、Tikcet
/// <summary>
/// 校验缓存Token、Tikcet
/// </summary>
/// <returns></returns>
[ApiExplorerSettings(IgnoreApi = true)]
public async Task CheckTokenAndTikcet()
{
try
{
//获取缓存数据
var tokendata = _memoryCache.Get<CgibinTokenResponse>(ResCgibinTokenKey)?.AccessToken;
var ticketdata = _memoryCache.Get<CgibinTicketGetTicketResponse>(TicketKey)?.Ticket;
//缓存中无数据调用微信接口获取Access token 和 ticket
if (string.IsNullOrWhiteSpace(tokendata) || string.IsNullOrWhiteSpace(ticketdata))
{
//获取Access token、ticket
await GetTokenAndTikcet();
}
}
catch
{
//获取Access token、ticket
await GetTokenAndTikcet();
}
}
#endregion
#region 获取Access token、ticket 放到缓存中
/// <summary>
/// 获取Access token、ticket 放到缓存
/// </summary>
/// <returns></returns>
[ApiExplorerSettings(IgnoreApi = true)]
public async Task GetTokenAndTikcet()
{
//获取Access token
var resCgibinToken = await _wechatApiClient.ExecuteCgibinTokenAsync(new CgibinTokenRequest());
_memoryCache.Set(ResCgibinTokenKey, resCgibinToken, DateTimeOffset.Now.AddHours(1));
//获取ticket
var token = _memoryCache.Get<CgibinTokenResponse>(ResCgibinTokenKey);
var request = new CgibinTicketGetTicketRequest()
{
AccessToken = token?.AccessToken
};
var ticket = await _wechatApiClient.ExecuteCgibinTicketGetTicketAsync(request);
if (ticket.IsSuccessful())
{
_memoryCache.Set(TicketKey, ticket, DateTimeOffset.Now.AddHours(1));
}
}
#endregion
#region 获取微信配置JS-SDK `wx.config`
/// <summary>
/// 获取微信配置JS-SDK `wx.config`
/// </summary>
/// <param name="url">分享地址</param>
/// <param>分享地址必须与当前微信访问地址相同,否则分享失败</param>
/// <returns></returns>
[HttpPost("GetWXConfig")]
public async Task<ApiResult<dynamic>> GenConfigPara(string url)
{
var res = new ApiResult<dynamic>();
//获取Access token 放到缓存
await CheckTokenAndTikcet();
try
{
var response = _memoryCache.Get<CgibinTicketGetTicketResponse>(TicketKey);
if (!response.IsSuccessful())
{
res.success = false;
res.data = response.ErrorMessage;
res.code = 400;
res.message = response.ErrorMessage;
return await Task.Run(() => res);
}
//获取JS-SDK `wx.config` 所需的参数
var data = _wechatApiClient.GenerateParametersForJSSDKConfig(response.Ticket, url);
res.success = true;
res.data = data;
res.code = 200;
return await Task.Run(() => res);
}
catch (Exception ex)
{
res.success = false;
res.data = ex.Message;
res.code = 400;
res.message = ex.Message;
return await Task.Run(() => res);
}
}
#endregion
/// <summary>
/// API 返回JSON字符串
/// </summary>
/// <typeparam name="T"></typeparam>
public class ApiResult<T> where T : class
{
/// <summary>
/// 是否成功
/// </summary>
public bool success { get; set; } = true;
/// <summary>
/// 信息
/// </summary>
public string message { get; set; }
/// <summary>
/// 状态码
/// </summary>
public int code { get; set; } = 200;
/// <summary>
/// 数据集
/// </summary>
public T data { get; set; }
}
}
注意:微信对 access_token、jsapi_ticket 的获取,每天是有次数限制的,其每次获取的有效时长为两小时。因此我们需要将获取到的 access_token 放到系统缓存中,统一进行更新!
3.具体操作过程,在微信内打开访问地址,将其网页收藏,然后从收藏处进行转发即可!