或第三方网站分享转发后,打开提示 “无法打开该页面,不支持打开” 或 “页面不存在”(IOS 苹果系统打开是空白页,安卓系统会有提示)超详细排查
Posted 王佳斌
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了或第三方网站分享转发后,打开提示 “无法打开该页面,不支持打开” 或 “页面不存在”(IOS 苹果系统打开是空白页,安卓系统会有提示)超详细排查相关的知识,希望对你有一定的参考价值。
前言
本文从 [初步排查] 到 [代码排查],完美解决 因各种原因导致 webview 页面分享后,用户打不开提示错误 这类问题,
您只需要按照排查步骤一步一步的走,从检查微信小程序后台配置是否正确,再到检查分享、跳转代码。
页面由 webview 组件承载,点击右上角 ··· 分享转发出去后,用户却无法打开(如下图真机所示),
大部分情况下,ios 苹果系统打开是空白页,安卓系统才会有提示!
就算您提示的内容跟下图不一致或相似,照样可以解决您的问题(通用解决方案)。
初步排查
首要检查的就是 微信小程序后台 的配置,如果配置都不对那么一定会报错!
<
微信朋友圈转发第三方网站带缩略图实现
前情提要
有时候我们会在朋友圈看到如下两种转发情况:一种是前面带缩略图的 ,一种是无缩略图的,当然有缩略图的不管是从用户体验,还是网站推广运营方都是更优的选择。
那我们看看微信分享朋友圈缩略图是 怎么一回事呢
注:微信6.5.5版本后,微信调整了分享规则。以前的没有通过认证公众号jssdk注入分享的都不是官方认可的分享。
必要前提:① 所打开的分享网页 域名必须经过备案(备案过的二级域名也行) ②公众号后台基本配置里面获取AppID和AppSecret 以添加服务器IP到白名单 显示如下:
代码实现
在HTML的Body下 加入如下代码:
<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script> <script> var dataForWeixin = { appId: \'@ViewBag.appid\', url: \'@ViewBag.url\', jsapiTicket:\'@ViewBag.jsapiTicket\', title: \'转发标题\', imgUrl: \'服务器上需要显示的图片路径\', timestamp: \'@ViewBag.timestamp\', nonceStr: \'@ViewBag.nonceStr\', signature: \'@ViewBag.signature\', jsApiList: [\'onMenuShareTimeline\',\'onMenuShareAppMessage\'], callback: function () { } }; wx.config({ debug: false, appId: dataForWeixin.appId, timestamp: dataForWeixin.timestamp, nonceStr: dataForWeixin.nonceStr, signature: dataForWeixin.signature, jsApiList: dataForWeixin.jsApiList }); </script> <div style="height:0px;overflow:hidden;"> <img src="服务器上需要显示的图片路径" /> </div>
在控制器中加入一个 获取微信分享接口的参数如下(再程序入口调用此方法即可)
/// <summary> /// 获取微信分享接口参数 /// </summary> public void GetWX() { string app_id = ConfigHelper.AppId; string AppSecret = ConfigHelper.AppSecret; writeLog.WriteLogs("app_id,AppSecret:" + app_id + AppSecret + ""); Wx_helper jssdk = new Wx_helper(app_id, AppSecret); Hashtable ht = jssdk.getSignPackage(); // 遍历哈希表 foreach (DictionaryEntry de in ht) { if (de.Key.ToString() == "appId") { ViewBag.appid = de.Value.ToString(); } if (de.Key.ToString() == "nonceStr") { ViewBag.nonceStr = de.Value.ToString(); } if (de.Key.ToString() == "timestamp") { ViewBag.timestamp = de.Value.ToString(); } if (de.Key.ToString() == "url") { ViewBag.url = de.Value.ToString(); } if (de.Key.ToString() == "signature") { ViewBag.signature = de.Value.ToString(); } if (de.Key.ToString() == "jsapiTicket") { ViewBag.jsapiTicket = de.Value.ToString(); } } }
上述代码中 Wx_helper(微信接口类)如下:
/// <summary> /// 微信接口类 /// </summary> public class Wx_helper : DBBase { private string appId; private string appSecret; private DataTable DT; public Wx_helper(string appId, string appSecret) { this.appId = appId; this.appSecret = appSecret; } //得到数据包,返回使用页面 public System.Collections.Hashtable getSignPackage() { string jsapiTicket = getJsApiTicket(); string url_req = HttpContext.Current.Request.Url.ToString(); //当前网页的URL string pageurl = HttpContext.Current.Request.Url.AbsoluteUri; writeLog.WriteLogs("当前网页的URL:" + pageurl + ""); //string url = "http://" + HttpContext.Current.Request.ServerVariables["Http_Host"] + HttpContext.Current.Request.ApplicationPath; string timestamp = Convert.ToString(ConvertDateTimeInt(DateTime.Now)); string nonceStr = createNonceStr(); // 这里参数的顺序要按照 key 值 ASCII 码升序排序 string rawstring = "jsapi_ticket=" + jsapiTicket + "&noncestr=" + nonceStr + "×tamp=" + timestamp + "&url=" + pageurl + ""; writeLog.WriteLogs("rawstring:" + rawstring + ""); string signature = SHA1_Hash(rawstring); System.Collections.Hashtable signPackage = new System.Collections.Hashtable(); signPackage.Add("appId", appId); signPackage.Add("nonceStr", nonceStr); signPackage.Add("timestamp", timestamp); signPackage.Add("url", pageurl); signPackage.Add("signature", signature); signPackage.Add("jsapiTicket", jsapiTicket); return signPackage; } //创建随机字符串 private string createNonceStr() { int length = 16; string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; string str = ""; Random rad = new Random(); for (int i = 0; i < length; i++) { str += chars.Substring(rad.Next(0, chars.Length - 1), 1); } return str; } //SHA1哈希加密算法 public string SHA1_Hash(string str_sha1_in) { SHA1 sha1 = new SHA1CryptoServiceProvider(); byte[] bytes_sha1_in = System.Text.UTF8Encoding.Default.GetBytes(str_sha1_in); byte[] bytes_sha1_out = sha1.ComputeHash(bytes_sha1_in); string str_sha1_out = BitConverter.ToString(bytes_sha1_out); str_sha1_out = str_sha1_out.Replace("-", "").ToLower(); return str_sha1_out; } //得到ticket 如果文件里时间 超时则重新获取 private string getJsApiTicket() { //这里我从数据库读取 string strSql = "select jsapi_ticket,ticket_expires,add_time from dt_weixin_jsapiticket where ID=1"; DataSet ds = sqlhelp.ExecuteDataSet(strSql); DT = ds.Tables[0]; int expire_time = Convert.ToInt32(DT.Rows[0]["ticket_expires"]); string ticket = DT.Rows[0]["jsapi_ticket"].ToString(); string error = string.Empty; string accessToken = new WXCRMComm().GetAccessToken(out error); //获取系统的全局token writeLog.WriteLogs("获取系统的全局token :" + accessToken + "," + error + ""); if (string.IsNullOrEmpty(error)) { writeLog.WriteLogs("GetAccessToken:" + error + ""); //计算时间判断是否过期 TimeSpan ts = DateTime.Now - DateTime.Parse(DT.Rows[0]["add_time"].ToString()); double chajunSecond = ts.TotalSeconds; if (chajunSecond >= 1200) { writeLog.WriteLogs("jsapiticket计算时间判断是否过期:已过期"); string url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + accessToken + "&type=jsapi"; Jsapi api = JsonConvert.DeserializeObject<Jsapi>(httpGet(url)); ticket = api.ticket; if (ticket != "") { expire_time = ConvertDateTimeInt(DateTime.Now) + 1200; //存入数据库操作 strSql = " update dt_weixin_jsapiticket set jsapi_ticket=\'" + ticket + "\',ticket_expires=\'" + expire_time + "\',add_time=\'" + DateTime.Now + "\' where ID=1"; sqlhelp.ExecuteNonQuery(strSql); } } writeLog.WriteLogs("jsapiticket计算时间判断是否过期:没过期"); } return ticket; } /// <summary> /// 将c# DateTime时间格式转换为Unix时间戳格式 /// </summary> /// <param name="time">时间</param> /// <returns>double</returns> public int ConvertDateTimeInt(System.DateTime time) { int intResult = 0; System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1)); intResult = Convert.ToInt32((time - startTime).TotalSeconds); return intResult; } //发起一个http请球,返回值 private string httpGet(string url) { try { WebClient MyWebClient = new WebClient(); MyWebClient.Credentials = CredentialCache.DefaultCredentials;//获取或设置用于向Internet资源的请求进行身份验证的网络凭据 Byte[] pageData = MyWebClient.DownloadData(url); //从指定网站下载数据 string pageHtml = System.Text.Encoding.Default.GetString(pageData); //如果获取网站页面采用的是GB2312,则使用这句 return pageHtml; } catch (WebException webEx) { Console.WriteLine(webEx.Message.ToString()); return null; } } #region 创建Json序列化 及反序列化类目 // /// <summary> /// 创建JSon类 保存文件 jsapi_ticket.json /// </summary> public class JSTicket { public string jsapi_ticket { get; set; } public double expire_time { get; set; } } /// <summary> /// 创建 JSon类 保存文件 access_token.json /// </summary> public class AccToken { public string access_token { get; set; } public double expires_in { get; set; } } /// <summary> /// 创建从微信返回结果的一个类 用于获取ticket /// </summary> public class Jsapi { public int errcode { get; set; } public string errmsg { get; set; } public string ticket { get; set; } public string expires_in { get; set; } } #endregion }
上述代码中负责获取和刷新的 WXCRMComm 类如下:
/// <summary> /// 负责获取或刷新AccessToken /// </summary> public class WXCRMComm { public WXCRMComm() { } private string appid; private string appsecret; BLLweixin_access_token tokenBLL = new BLL.BLLweixin_access_token(); //账户AccessToken 此处根据自己项目单独特别处理 BLLweixin_account accountBLL = new BLL.BLLweixin_account(); //公众平台账户 此处根据自己项目单独特别处理 /// <summary> /// 及时获得access_token值 /// access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token。正常情况下access_token有效期为7200秒, /// 重复获取将导致上次获取的access_token失效。 /// 每日限额获取access_token.我们将access_token保存到数据库里,间隔时间为20分钟,从微信公众平台获得一次。 /// </summary> public string GetAccessToken(out string error) { string access_token = string.Empty; error = string.Empty; try { Model.weixin_account accountModel = accountBLL.GetModel(); //公众平台账户信息 if (accountModel == null || string.IsNullOrEmpty(accountModel.appid) || string.IsNullOrEmpty(accountModel.appsecret)) { error = "AppId或者AppSecret未填写,请在补全信息!"; return string.Empty; } //没有找到该账户则获取AccessToKen写入存储1200秒 if (!tokenBLL.Exists()) { var result = Senparc.Weixin.MP.CommonAPIs.CommonApi.GetToken(accountModel.appid, accountModel.appsecret); access_token = result.access_token; tokenBLL.Add(access_token); return access_token; } //获取公众账户的实体 Model.weixin_access_token tokenModel = tokenBLL.GetModel(); //计算时间判断是否过期 TimeSpan ts = DateTime.Now - tokenModel.add_time; double chajunSecond = ts.TotalSeconds; if (string.IsNullOrEmpty(tokenModel.access_token) || chajunSecond >= tokenModel.expires_in) { writeLog.WriteLogs("GetAccessToken:重新修改"); //从微信平台重新获得AccessToken var result = Senparc.Weixin.MP.CommonAPIs.CommonApi.GetToken(accountModel.appid, accountModel.appsecret); access_token = result.access_token; //更新到数据库里的AccessToken tokenModel.access_token = access_token; tokenModel.add_time = DateTime.Now; bool ret=tokenBLL.Update(tokenModel); writeLog.WriteLogs("GetAccessToken:重新修改"+ ret + ""); } else { writeLog.WriteLogs("GetAccessToken:获取旧的"); access_token = tokenModel.access_token; } } catch (Exception ex) { error = "获取AccessToken出错:" + ex.Message; } return access_token; } /// <summary> ///【强制刷新】access_token值 /// access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token。正常情况下access_token有效期为7200秒, /// 重复获取将导致上次获取的access_token失效。 /// 每日限额获取access_token.我们将access_token保存到数据库里,间隔时间为20分钟,从微信公众平台获得一次。 /// </summary> /// <returns></returns> public string FlushAccessToken(out string error) { string access_token = string.Empty; error = string.Empty; try { Model.weixin_account accountModel = accountBLL.GetModel(); //公众平台账户信息 if (string.IsNullOrEmpty(accountModel.appid) || string.IsNullOrEmpty(accountModel.appsecret)) { error = "AppId或者AppSecret未填写,请在补全信息!"; return ""; } var result = Senparc.Weixin.MP.CommonAPIs.CommonApi.GetToken(accountModel.appid, accountModel.appsecret); access_token = result.access_token; //没有找到该账户则获取AccessToKen写入存储1200秒 if (!tokenBLL.Exists()) { tokenBLL.Add(access_token); } else { //获取公众账户的实体 Model.weixin_access_token tokenModel = tokenBLL.GetModel(); //更新到数据库里的AccessToken tokenModel.access_token = access_token; tokenModel.add_time = DateTime.Now; tokenBLL.Update(tokenModel); } } catch (Exception ex) { error = "获得AccessToken出错:" + ex.Message; } return access_token; } /// <summary> /// 获得所有关注用户的openid字符串(别的方法调用此方法) /// </summary> private IList<string> BaseUserOpenId(out string error) { IList<string> ret = new List<string>(); string access_token = GetAccessToken(out error); if (error != "") { return null; } Senparc.Weixin.MP.AdvancedAPIs.User.OpenIdResultJson openidJson = Senparc.Weixin.MP.AdvancedAPIs.UserApi.Get(access_token, string.Empty); if (openidJson.count == openidJson.total) { ret = openidJson.data.openid; } else { GetNextUserOpenId(openidJson.next_openid, ret); } return ret; } /// <summary> /// (基础方法)获得所有关注用户的openid字符串(递归算法) /// </summary> private void GetNextUserOpenId(string nexOpenid, IList<string> openidList) { string err = string.Empty; string access_token = GetAccessToken(out err); Senparc.Weixin.MP.AdvancedAPIs.User.OpenIdResultJson openidJson = Senparc.Weixin.MP.AdvancedAPIs.UserApi.Get(access_token, nexOpenid); if (openidJson == null || openidJson.count <= 0) { return; } else { for (int i = 0; i < openidJson.data.openid.Count; i++) { openidList.Add(openidJson.data.openid[i]); } GetNextUserOpenId(openidJson.next_openid, openidList); } } #region 消息群发处理=================================== /// <summary> /// 上传永久素材 /// </summary> public string UploadForeverMedia(string imgFullPath, out string error) { string accessToken = GetAccessToken(out error); if (!string.IsNullOrEmpty(error)) { return string.Empty; } var result = Senparc.Weixin.MP.AdvancedAPIs.MediaApi.UploadForeverMedia(accessToken, imgFullPath); if (result.errcode == 0) { return result.media_id; } error = result.errmsg; return string.Empty; } /// <summary> /// 删除永久素材 /// </summary> public bool DeleteForeverMedia(string mediaId, out string error) { string accessToken = GetAccessToken(out error); if (!string.IsNullOrEmpty(error)) { return false; } var result = Senparc.Weixin.MP.AdvancedAPIs.MediaApi.DeleteForeverMedia(accessToken, mediaId); if (result.errcode != 0) { error = result.errmsg; return false; } error = string.Empty; return true; } /// <summary> /// 群发消息 /// </summary> public bool SendGroupMessageByGroupId(List<Senparc.Weixin.MP.AdvancedAPIs.GroupMessage.NewsModel> ls, out string error) { string accessToken = GetAccessToken(out error); //新增素材 var result1 = Senparc.Weixin.MP.AdvancedAPIs.MediaApi.UploadNews(accessToken, 10000, ls.ToArray()); if (result1.errcode != 0) { error = result1.errmsg; return false; } //群发消息 var result2 = Senparc.Weixin.MP.AdvancedAPIs.GroupMessageApi.SendGroupMessageByGroupId(accessToken, "0", result1.media_id, Senparc.Weixin.MP.GroupMessageType.mpnews, true); if (result2.errcode != 0) { error = result2.errmsg; return false; } error = string.Empty; return true; } #endregion }
另外,微信接口类 继承 了 DBBase ,里面只有连接数据库对象,这个可以根据自己项目特点和需要处理 这里只做演示:
public class DBBase { protected static string connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString; public SqlHelper sqlhelp = new SqlHelper(connectionString); }
上述代码中SqlHelper 通用帮助类 也贴一下吧,仅供参考
/// <summary> /// SqlHelper操作类 /// </summary> public sealed partial class SqlHelper { /// <summary> /// 批量操作每批次记录数 /// </summary> public static int BatchSize = 2000; /// <summary> /// 超时时间 /// </summary> public static int CommandTimeOut = 600; /// <summary> ///初始化SqlHelper实例 /// </summary> /// <param name="connectionString">数据库连接字符串</param> public SqlHelper(string connectionString) { this.ConnectionString = connectionString; } /// <summary> /// 数据库连接字符串 /// </summary> public string ConnectionString { get; set; } #region 实例方法 #region ExecuteNonQuery /// <summary> /// 执行SQL语句,返回影响的行数 /// </summary>以上是关于或第三方网站分享转发后,打开提示 “无法打开该页面,不支持打开” 或 “页面不存在”(IOS 苹果系统打开是空白页,安卓系统会有提示)超详细排查的主要内容,如果未能解决你的问题,请参考以下文章
微信打开网址后自动调用手机自带默认浏览器或提示选择浏览器打开如何实现