自定义第三方网站微信分享封面图、标题和摘要
如果你尝试过把第三方网站分享到微信,可能会发现,很多网站分享到微信之后显示的标题、缩略图和摘要都是默认值。
在默认情况下,微信会抓取网页的 title,摘要会显示 URL,缩略图则是默认图片,看起来很不美观,信息也不丰富。
作为网站开发者,我当然是希望能自定义微信分享卡片的。研究了下文档,发现这个过程还是有些麻烦的,并且需要后端参与。于是鼓捣了一下,现在已经可以分享出好看的卡片了。
整体思路
微信提供了 JSSDK 用于自定义,前端网页需要引入这个 SDK。SDK 需要在初始化和鉴权之后才能使用,所以这里有一个生成签名和鉴权的流程,而生成签名需要在后端进行。后端鉴权是使用的 JS API ticket,这个 ticket 要用 access token 换取。因此,我们实现这个 feature 的整体思路是:
- 后端获取 access token,并使用 access token 换取 JS API ticket。
- 使用 ticket 生成签名,将签名返回前端。
- 前端 SDK 使用签名进行鉴权和初始化。
- 设置文章分享的标题、封面图和摘要。
获取 JS API ticket
在获取 JS API ticket 之前,我们需要先获取 access token,这里的步骤可以参考这一篇文章。
这个 ticket 和 access token 很相似,都有一定长度的有效期,也都有获取频率的限制,所以当然也需要使用 Redis 进行缓存。需要取 ticket 时,优先从 Redis 获取,取到了就直接返回;如果没取到,再用 access token 向微信接口请求。
module.exports = async (ctx) => {
try {
let jsApiTicket = await ctx.redis.get('wechatJsApiTicket');
if (!jsApiTicket) {
const accessToken = await getAccessToken(ctx);
if (!accessToken) {
throw new Error('get access token error');
}
const res = await axios.get(`${api.wechat.getJsApiTicket}?access_token=${accessToken}&type=jsapi`);
if (res?.status !== 200) {
throw new Error('get js api ticket error');
}
jsApiTicket = res?.data?.ticket;
const expiresIn = res?.data?.expires_in;
if (!jsApiTicket) {
throw new Error('get js api ticket error');
} else {
await ctx.redis.set('wechatJsApiTicket', jsApiTicket);
await ctx.redis.expire('wechatJsApiTicket', expiresIn);
}
}
return Promise.resolve(jsApiTicket);
} catch (err) {
return Promise.reject(err);
}
};
生成签名
微信 JS SDK 每个不同 URL 的页面都需要进行初始化和鉴权,需要把 URL 作为参数参与签名的生成。所以,前端向后端发请求时需要把当前页面的 URL 带上。
request.get(api.service.wechat.getJsSdkSignature, {
params: {
url: window.location.href,
},
})
后端接到前端请求后,取出 URL 参数,按照微信提供的算法生成签名。
module.exports = async (ctx, url) => {
try {
// 刚才封装的获取 JS API ticket 的方法
const jsApiTicket = await getJsApiTicket(ctx);
if (!jsApiTicket) {
throw new Error('get js api ticket error');
}
// 生成随机字符串
const randomString = RandomString.generate(16);
// 生成时间戳
const timestamp = (Number(new Date()) / 1000).toFixed(0);
// 按照微信给定的格式拼接几个生成签名的参数
const rawString = `jsapi_ticket=${jsApiTicket}&noncestr=${randomString}×tamp=${timestamp}&url=${url}`;
// 使用 sha1 算法生成签名
const signature = sha1(rawString);
// 把签名及生成签名的随机字符串和时间戳返回
return Promise.resolve({
randomString,
timestamp,
signature,
});
} catch (err) {
return Promise.reject(err);
}
};
这里生成签名除了要用到前端页面 URL 和 JS API ticket 之外,还需要使用一个随机字符串和一个时间戳。需要注意的是,时间戳需要换成以秒为单位的。另外,生成的随机字符串和和时间戳也需要返回给前端。
前端 SDK 鉴权和初始化
第一步,前端首先需要引入这个 SDK。从微信官方地址引入 JS 文件即可。
script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js">script>
引入 JS 文件之后,wx
这个对象就会被挂载在 window 上。如果你使用的是 TS,那么你需要为 window 添加类型定义。
interface WechatConfigProps {
debug?: boolean;
appId: string;
timestamp: number;
nonceStr: string;
signature: string;
jsApiList: string[];
}
interface UpdateAppMessageShareDataProps {
title: string;
desc: string;
link: string;
imgUrl: string;
}
interface UpdateTimelineShareDataProps {
title: string;
link: string;
imgUrl: string;
}
interface WechatObject {
config: (props: WechatConfigProps) => void;
ready: (callback: () => void) => void;
updateAppMessageShareData: (props: UpdateAppMessageShareDataProps) => void;
updateTimelineShareData: (props: UpdateTimelineShareDataProps) => void;
}
interface Window {
wx: WechatObject;
}
在向后端请求签名后,就可以在前端初始化微信 JS SDK 了。
request.get(api.service.wechat.getJsSdkSignature, {
params: {
url: window.location.href,
},
}).then((res) => {
if (res.status === 200) {
window.wx.config({
appId: '微信公众号的 appid',
timestamp: Number(res.data.timestamp),
nonceStr: res.data.randomString,
signature: res.data.signature,
jsApiList: ['updateAppMessageShareData', 'updateTimelineShareData'],
});
}
});
设置分享到微信的标题、封面图和摘要
微信 JS SDK 初始化完成之后,就可以设置分享的样式和信息了。这里需要注意,window.wx.config
是一个异步函数,并且没有提供回调或者 Promise 形式的返回。如果我们调用完 config
之后立即设置分享信息,并不会起作用。不过这个 SDK 提供了 ready
函数,它的参数是一个函数,放在这个函数里面的内容会保证在 config
函数执行成功之后再执行。
window.wx.ready(() => {
window.wx.updateAppMessageShareData({
title: '文章标题',
desc: '摘要',
link: '网页 URL',
imgUrl: '分享封面图',
});
window.wx.updateTimelineShareData({
title: '文章标题',
link: '网页 URL',
imgUrl: '分享封面图',
});
});
在 wx.ready
的回调中分别为消息分享卡片和朋友圈分享设置好标题、摘要、URL 和缩略图之后,就大功告成了。