官方文档在这里:https://developers.weixin.qq.com/miniprogram/dev/component/web-view.html
如果只是使用wx.miniProgram.navigateTo这种导航的接口,jssdk可以不用做配置,引用js后直接使用就行,如果chooseImage这种,就需要获取配置了,步骤如下:
- 先在后端通过AppID和AppSecret取到access_token,并缓存access_token
- 再通过access_token获取到jsapi_ticket,jsapi_ticket的值也要缓存
- 使用jsapi_ticket、当前URL、随机字符串、1970年1月1日00:00:00至今的秒数生成签名及及配置,返回给前端,签名记得使用https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign校验是否正确
- 前端拿到config后放到wx.config里执行
- 在wx.ready中注册事件
步骤比较多但是并不复杂,主要是这其中几个坑需要避开别踩到:
- AppID和AppSecret不能用小程序的,要用公众号的,否则会报invalid url domain的错误,官方文档上并没有说明这一点,所以这个坑非常深非常隐蔽,另外这个公众号要不要与小程序关联还不清楚,我用的是关联的。
- 签名用到的当前URl不要解码,拿到的是什么就用什么,否则即使签名校验通过也会报签名错误
- 如果你用了chooseImage这个api,需要获取到选择的图片的话,官方文档上说是在success的tempFilePaths参数里,实际上并不是,是在localIds里面
以下是部分代码:
.Net后台:
#region JS-SDK配置
/// <summary>
/// 获取access_token(AppID和AppSecret不能使用小程序的,要使用公众号的)
/// </summary>
/// <returns>access_token</returns>
public static string GetAccessToken()
{
object obj = NetCacheHelper.GetCache("access_token");
string access_token = "";
if (obj == null)
{
string AppID = SystemHelper.GetSystemStrValue("AppID");
string AppSecret = SystemHelper.GetSystemStrValue("AppSecret");
string url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + AppID + "&secret=" + AppSecret;
string value = HttpHelper.Get(url);
WxAccessTokenModel model = JsonConvert.DeserializeObject<WxAccessTokenModel>(value);
NetCacheHelper.SetCache("access_token", model.access_token, TimeSpan.FromSeconds(model.expires_in - 60));
access_token = model.access_token;
}
else
{
access_token = obj.ToString();
}
return access_token;
}
/// <summary>
/// 获取jsapi_ticket
/// </summary>
/// <returns>jsapi_ticket</returns>
public static string GetTicket()
{
string access_token = GetAccessToken();
object obj = NetCacheHelper.GetCache("jsapi_ticket");
string jsapi_ticket = "";
if (obj == null)
{
string url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+ access_token + "&type=jsapi";
string value = HttpHelper.Get(url);
WxTicketModel model = JsonConvert.DeserializeObject<WxTicketModel>(value);
NetCacheHelper.SetCache("jsapi_ticket", model.ticket, TimeSpan.FromSeconds(model.expires_in - 60));
jsapi_ticket = model.ticket;
}
else
{
jsapi_ticket = obj.ToString();
}
return jsapi_ticket;
}
/// <summary>
/// 获取JSSDK的配置
/// </summary>
/// <param name="jsApiList">需要使用的JS接口列表</param>
/// <param name="isDebug">是否是调试模式</param>
/// <returns>JSSDK的配置</returns>
public static string GetJSSDKConfig(List<string> jsApiList, bool isDebug)
{
string url = HttpHelper.GetCurrentFullURL(true, false, "weixin");
return GetJSSDKConfig(jsApiList, isDebug, url);
}
/// <summary>
/// 获取JSSDK的配置
/// </summary>
/// <param name="jsApiList">需要使用的JS接口列表</param>
/// <param name="isDebug">是否是调试模式</param>
/// <param name="url">页面URL</param>
/// <returns>JSSDK的配置</returns>
public static string GetJSSDKConfig(List<string> jsApiList, bool isDebug, string url)
{
#region 准备签名必须要的参数
string noncestr = DataHelper.GetRandomString(16, true, true, true, false, "");
string jsapi_ticket = GetTicket();
string timestamp = DateHelper.Get19700101seconds(false).ToString();
#endregion
#region 进行签名
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.Add("noncestr", noncestr);
dic.Add("jsapi_ticket", jsapi_ticket);
dic.Add("timestamp", timestamp);
dic.Add("url", url);
dic = dic.OrderBy(t => t.Key).ToDictionary(m => m.Key, m => m.Value);
string sign = dic.Aggregate("", (current, d) => current + (d.Key + "=" + d.Value + "&")).Trim('&');
string signature = EncryptionHelper.GetSHA1(sign).ToLower();
#endregion
#region 获得配置
WxJSSDKConfig config = new WxJSSDKConfig();
config.appId = SystemHelper.GetSystemStrValue("AppID");
config.debug = isDebug;
config.jsApiList = jsApiList;
config.nonceStr = noncestr;
config.signature = signature;
config.timestamp = timestamp;
#endregion
string json = JsonConvert.SerializeObject(config);
return json;
}
#endregion
.Net后台的部分帮助类和模型类:
/// <summary>
/// 获取微信access_token返回的数据格式
/// </summary>
public class WxAccessTokenModel
{
/// <summary>
/// access_token
/// </summary>
public string access_token { get; set; }
/// <summary>
/// expires_in
/// </summary>
public int expires_in { get; set; }
}
/// <summary>
/// 访问微信ticket返回的数据格式
/// </summary>
public class WxTicketModel
{
/// <summary>
/// expires_in
/// </summary>
public int expires_in { get; set; }
/// <summary>
/// ticket
/// </summary>
public string ticket { get; set; }
/// <summary>
/// 错误代码
/// </summary>
public string errcode { get; set; }
/// <summary>
/// 错误消息
/// </summary>
public string errmsg { get; set; }
}
/// <summary>
/// 微信JS-SDK的配置
/// </summary>
public class WxJSSDKConfig
{
/// <summary>
/// 开启调试模式
/// </summary>
public bool debug { get; set; }
/// <summary>
/// 公众号的唯一标识
/// </summary>
public string appId { get; set; }
/// <summary>
/// 生成签名的时间戳
/// </summary>
public string timestamp { get; set; }
/// <summary>
/// 生成签名的随机串
/// </summary>
public string nonceStr { get; set; }
/// <summary>
/// 签名
/// </summary>
public string signature { get; set; }
/// <summary>
/// 需要使用的JS接口列表
/// </summary>
public List<string> jsApiList { get; set; }
}
///<summary>
///生成随机字符串
///</summary>
///<param name="length">目标字符串的长度</param>
///<param name="useNum">是否包含数字,1=包含,默认为包含</param>
///<param name="useLow">是否包含小写字母,1=包含,默认为包含</param>
///<param name="useUpp">是否包含大写字母,1=包含,默认为包含</param>
///<param name="useSpe">是否包含特殊字符,1=包含,默认为不包含</param>
///<param name="custom">要包含的自定义字符,直接输入要包含的字符列表</param>
///<returns>指定长度的随机字符串</returns>
public static string GetRandomString(int length, bool useNum, bool useLow, bool useUpp, bool useSpe, string custom)
{
byte[] b = new byte[4];
new System.Security.Cryptography.RNGCryptoServiceProvider().GetBytes(b);
Random r = new Random(BitConverter.ToInt32(b, 0));
string s = null, str = custom;
if (useNum == true) { str += "0123456789"; }
if (useLow == true) { str += "abcdefghijklmnopqrstuvwxyz"; }
if (useUpp == true) { str += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; }
if (useSpe == true) { str += "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; }
for (int i = 0; i < length; i++)
{
s += str.Substring(r.Next(0, str.Length - 1), 1);
}
return s;
}
/// <summary>
/// 获取1970年1月1日00:00:00至今的秒数
/// </summary>
/// <param name="keepDecimal">是否保留小数点</param>
/// <returns>1970年1月1日00:00:00至今的秒数</returns>
public static double Get19700101seconds(bool keepDecimal)
{
DateTime time = DateTime.Parse("1970-01-01 00:00:00");
DateTime now = DateTime.Now;
TimeSpan ts = now - time;
if (keepDecimal)
{
return ts.TotalSeconds;
}
else
{
return Math.Floor(ts.TotalSeconds);
}
}
/// <summary>
/// SHA1加密
/// </summary>
/// <param name="source">原字符串</param>
/// <returns>加密后的字符串</returns>
public static string GetSHA1(string source)
{
try
{
SHA1 sha1 = new SHA1CryptoServiceProvider();
byte[] bytes_in = Encoding.UTF8.GetBytes(source);
byte[] bytes_out = sha1.ComputeHash(bytes_in);
sha1.Dispose();
string result = BitConverter.ToString(bytes_out);
result = result.Replace("-", "");
return result;
}
catch (Exception)
{
return "";
}
}
.Net调用:
string config = WxHelper.GetJSSDKConfig(new List<string>() { "chooseImage", "previewImage", "uploadImage" }, false);
前端JS:
var config = $("#Config").val();
config = JSON.parse(config);
wx.config(config);
wx.ready(function () {
document.querySelector('#addFile').onclick = function () {
wx.chooseImage({
success: function (res) {
wx.previewImage({
current: res.localIds[0], // 当前显示图片的http链接
urls: res.localIds// 需要预览的图片http链接列表
})
}
});
};
});
wx.error(function (res) {
});