微信公众平台开发:JS-SDK之分享功能整理

来源:互联网 发布:sql查询结果导出excel 编辑:程序博客网 时间:2024/05/17 23:56

原理

其实我也不懂,就不乱说了,大致就是微信公众账号平台有提供一个js-sdk开发文档,有一定的开发规范,对又到分享等违规行为也有严格要求。按着开发文档做,区别就是中通过后台验证一下APPID与SECRET是否正确,是否授权等,恶心之处就在后台验证时。JS-SDK文档在 这里 .  
步骤(此处未尝试):

  首先下载官方的示例代码:http://demo.open.weixin.qq.com/jssdk/sample.zip

       此代码包括:jweixin-1.js、demo.js、style.css、jssdk.html。以上代码略加修改即可以用,下面是具体的实现方法四步曲:

       步骤一:绑定域名,先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”;

       步骤二:引入JS文件,在需要调用JS接口的页面引入JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.0.0.js;

       步骤三:通过config接口注入权限验证配置,所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用);

        步骤四:通过ready接口处理成功验证。

        因此,这里最重要的一步是步骤三,获取config配置的签名signature ,如下:

wx.config({

    debug: true,  // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。    appId: '',    // 必填,公众号的唯一标识    timestamp: ,  // 必填,生成签名的时间戳    nonceStr: '', // 必填,生成签名的随机串    signature: '',// 必填,签名,见附录1    jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2});

实现

在开发之前肯定是需要几个从公众平台账号中拿到的开发者id,如不知道是什么,找领导要不给就干嘛~~  好了,由于此处也是领导给的APPID编号这些,所以具体我也不清楚怎么来的。。。。  百度上应该可以找到吧~ 
(一)实体类 TokenJson
[java] view plain copy
  1. **  
  2.  * @author Allen  
  3.  * @version 1.0  
  4.  * 创建时间:2016412日 下午4:54:58  
  5.  */  
  6. public class TokenJson {  
  7.     private String access_token;  
  8.     private int expires_in;  
  9.       
  10.     public String getAccess_token() {  
  11.         return access_token;  
  12.     }  
  13.     public void setAccess_token(String access_token) {  
  14.         this.access_token = access_token;  
  15.     }  
  16.     public int getExpires_in() {  
  17.         return expires_in;  
  18.     }  
  19.     public void setExpires_in(int expires_in) {  
  20.         this.expires_in = expires_in;  
  21.     }  
  22.       
  23.       
  24. }  
实体类TicketJson
[java] view plain copy
  1. /** 
  2.  * @author Allen 
  3.  * @version 1.0 
  4.  * 创建时间:2016年4月12日 下午5:03:14 
  5.  */  
  6. public class TicketJson {  
  7.     private int errcode;  
  8.     private String errmsg;  
  9.     private String ticket;  
  10.     private String expires_in;  
  11.     public int getErrcode() {  
  12.         return errcode;  
  13.     }  
  14.     public void setErrcode(int errcode) {  
  15.         this.errcode = errcode;  
  16.     }  
  17.     public String getErrmsg() {  
  18.         return errmsg;  
  19.     }  
  20.     public void setErrmsg(String errmsg) {  
  21.         this.errmsg = errmsg;  
  22.     }  
  23.     public String getTicket() {  
  24.         return ticket;  
  25.     }  
  26.     public void setTicket(String ticket) {  
  27.         this.ticket = ticket;  
  28.     }  
  29.     public String getExpires_in() {  
  30.         return expires_in;  
  31.     }  
  32.     public void setExpires_in(String expires_in) {  
  33.         this.expires_in = expires_in;  
  34.     }  
  35. }  
HttpGetRequest用于连接验证服务器
[java] view plain copy
  1. import java.io.BufferedReader;  
  2. import java.io.InputStream;  
  3. import java.io.InputStreamReader;  
  4. import java.net.HttpURLConnection;  
  5. import java.net.URL;  
  6. import java.net.URLConnection;  
  7. /** 
  8.  * @author Allen 
  9.  * @version 1.0 创建时间:2016年4月12日 下午4:30:23 
  10.  */  
  11. public class HttpGetRequest {  
  12.   
  13.     /** 
  14.      * Get Request 
  15.      *  
  16.      * @return 
  17.      * @throws Exception 
  18.      */  
  19.     public static String doGet(String url) throws Exception {  
  20.         URL localURL = new URL(url);  
  21.         URLConnection connection = localURL.openConnection();  
  22.         HttpURLConnection httpURLConnection = (HttpURLConnection) connection;  
  23.         httpURLConnection.setRequestProperty("Accept-Charset""utf-8");  
  24.         httpURLConnection.setRequestProperty("Content-Type",  
  25.                 "application/text");  
  26.   
  27.         InputStream inputStream = null;  
  28.         InputStreamReader inputStreamReader = null;  
  29.         BufferedReader reader = null;  
  30.         StringBuffer resultBuffer = new StringBuffer();  
  31.         String tempLine = null;  
  32.   
  33.         if (httpURLConnection.getResponseCode() >= 300) {  
  34.             throw new Exception(  
  35.                     "HTTP Request is not success, Response code is "  
  36.                             + httpURLConnection.getResponseCode());  
  37.         }  
  38.   
  39.         try {  
  40.             inputStream = httpURLConnection.getInputStream();  
  41.             inputStreamReader = new InputStreamReader(inputStream);  
  42.             reader = new BufferedReader(inputStreamReader);  
  43.   
  44.             while ((tempLine = reader.readLine()) != null) {  
  45.                 resultBuffer.append(tempLine);  
  46.             }  
  47.   
  48.         } finally {  
  49.   
  50.             if (reader != null) {  
  51.                 reader.close();  
  52.             }  
  53.   
  54.             if (inputStreamReader != null) {  
  55.                 inputStreamReader.close();  
  56.             }  
  57.   
  58.             if (inputStream != null) {  
  59.                 inputStream.close();  
  60.             }  
  61.   
  62.         }  
  63.         return resultBuffer.toString();  
  64.     }  
  65. }  
如果有https SSL验证可用也可如下:
[java] view plain copy
  1. /**  
  2.  * 发起https请求并获取结果  
  3.  * @param requestUrl 请求地址  
  4.  * @param requestMethod 请求方式(GET/POST)  
  5.  * @param outputStr 提交的数据  
  6.  * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)  
  7.  */    
  8. public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {    
  9.     JSONObject jsonObject = null;    
  10.     StringBuffer buffer = new StringBuffer();    
  11.     try {    
  12.         // 创建SSLContext对象,并使用我们指定的信任管理器初始化    
  13.         TrustManager[] tm = { new MyX509TrustManager() };    
  14.         SSLContext sslContext = SSLContext.getInstance("SSL""SunJSSE");    
  15.         sslContext.init(null, tm, new java.security.SecureRandom());    
  16.         // 从上述SSLContext对象中得到SSLSocketFactory对象    
  17.         SSLSocketFactory ssf = sslContext.getSocketFactory();    
  18.   
  19.         URL url = new URL(requestUrl);    
  20.         HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();    
  21.         httpUrlConn.setSSLSocketFactory(ssf);    
  22.   
  23.         httpUrlConn.setDoOutput(true);    
  24.         httpUrlConn.setDoInput(true);    
  25.         httpUrlConn.setUseCaches(false);    
  26.             
  27.         // 设置请求方式(GET/POST)    
  28.         httpUrlConn.setRequestMethod(requestMethod);    
  29.         if ("GET".equalsIgnoreCase(requestMethod))    
  30.             httpUrlConn.connect();    
  31.   
  32.         // 当有数据需要提交时    
  33.         if (null != outputStr) {    
  34.             OutputStream outputStream = httpUrlConn.getOutputStream();    
  35.             // 注意编码格式,防止中文乱码    
  36.             outputStream.write(outputStr.getBytes("UTF-8"));    
  37.             outputStream.close();    
  38.         }    
  39.   
  40.         // 将返回的输入流转换成字符串    
  41.         InputStream inputStream = httpUrlConn.getInputStream();    
  42.         InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");    
  43.         BufferedReader bufferedReader = new BufferedReader(inputStreamReader);    
  44.   
  45.         String str = null;    
  46.         while ((str = bufferedReader.readLine()) != null) {    
  47.             buffer.append(str);    
  48.         }    
  49.         bufferedReader.close();    
  50.         inputStreamReader.close();    
  51.         // 释放资源    
  52.         inputStream.close();    
  53.         inputStream = null;    
  54.         httpUrlConn.disconnect();    
  55.         jsonObject = JSONObject.fromObject(buffer.toString());    
  56.     } catch (ConnectException ce) {    
  57.         Logger.getAnonymousLogger().info("Weixin server connection timed out.");    
  58.     } catch (Exception e) {    
  59.         Logger.getAnonymousLogger().info("信任管理器请求时..."+e);    
  60.     }    
  61.     return jsonObject;    
  62. }    
MyX509TrustManager 这个不用纠结是什么,它是用于验证SSL的一个自定义Class,此方法不可直接用,需要做一些相应更改,如HTTPS验证方式,SSL证书等等,自我调整应该即可。。。。 (自白:没有通过此方法实现HTTPS,而是使用了HttpGetRequest
WxParams 实体类 用于记录从weixin官网得到的token与ticket
[java] view plain copy
  1. /** 
  2.  * @author Allen 
  3.  * @version 1.0 
  4.  * 创建时间:2016年4月13日 下午3:53:57 
  5.  */  
  6. public class WxParams {  
  7.     public static String token;  
  8.     public static String tokenTime;  
  9.     public static String tokenExpires;  
  10.       
  11.     public static String ticket;  
  12.     public static String ticketTime;  
  13.     public static String ticketExpires;  
  14. }  
Sign 用于整理整合得到的值 (虽然我不懂,哈哈哈~~)
[java] view plain copy
  1. import java.io.UnsupportedEncodingException;  
  2. import java.security.MessageDigest;  
  3. import java.security.NoSuchAlgorithmException;  
  4. import java.util.Formatter;  
  5. import java.util.HashMap;  
  6. import java.util.Map;  
  7. import java.util.UUID;  
  8.   
  9. public class Sign {  
  10.       
  11.     public static Map<String, String> sign(String jsapi_ticket, String url) {  
  12.         Map<String, String> ret = new HashMap<String, String>();  
  13.         String nonce_str = create_nonce_str();  
  14.         String timestamp = create_timestamp();  
  15.         String string1;  
  16.         String signature = "";  
  17.   
  18.         //注意这里参数名必须全部小写,且必须有序  
  19.         string1 = "jsapi_ticket=" + jsapi_ticket +  
  20.                   "&noncestr=" + nonce_str +  
  21.                   "&timestamp=" + timestamp +  
  22.                   "&url=" + url;  
  23. //        System.out.println(string1);  
  24.         try  
  25.         {  
  26.             MessageDigest crypt = MessageDigest.getInstance("SHA-1");  
  27.             crypt.reset();  
  28.             crypt.update(string1.getBytes("UTF-8"));  
  29.             signature = byteToHex(crypt.digest());  
  30.         }  
  31.         catch (NoSuchAlgorithmException e)  
  32.         {  
  33.             e.printStackTrace();  
  34.         }  
  35.         catch (UnsupportedEncodingException e)  
  36.         {  
  37.             e.printStackTrace();  
  38.         }  
  39.   
  40.         ret.put("url", url);  
  41.         ret.put("jsapi_ticket", jsapi_ticket);  
  42.         ret.put("nonceStr", nonce_str);  
  43.         ret.put("timestamp", timestamp);  
  44.         ret.put("signature", signature);  
  45.   
  46.         return ret;  
  47.     }  
  48.   
  49.     private static String byteToHex(final byte[] hash) {  
  50.         Formatter formatter = new Formatter();  
  51.         for (byte b : hash)  
  52.         {  
  53.             formatter.format("%02x", b);  
  54.         }  
  55.         String result = formatter.toString();  
  56.         formatter.close();  
  57.         return result;  
  58.     }  
  59.   
  60.     private static String create_nonce_str() {  
  61.         return UUID.randomUUID().toString();  
  62.     }  
  63.   
  64.     private static String create_timestamp() {  
  65.         return Long.toString(System.currentTimeMillis() / 1000);  
  66.     }  
  67. }  
WxUtil 工具类 js中的入口
[java] view plain copy
  1. import java.util.Map;  
  2.   
  3. import net.sf.json.JSONObject;  
  4. import wrt.book.Json.TicketJson;  
  5. import wrt.book.Json.TokenJson;  
  6.   
  7. /** 
  8.  * @author Allen 
  9.  * @version 1.0 
  10.  * 创建时间:2016年4月12日 下午4:28:41 
  11.  */  
  12. public class WxUtil {  
  13.       
  14.     //此处的appid与wx.config 参数appId一致   微信公众账号提供给开发者的信息,以下同理  
  15.     public static String APPID = "wxb5571f0bxxxxxxxxx";  
  16.       
  17.     //同上  
  18.     public static String SECRET = "c73435d332dxxxxxxxxxxx";  
  19.       
  20.     private static TokenJson getAccess_token(){  
  21.   
  22.         String url = String.format("https://api.weixin.qq.com/xxxxxxxxxxxxxxxxxxxx",APPID,SECRET);  
  23.         try {  
  24.             String result = HttpGetRequest.doGet(url);  
  25.             System.out.println("微信服务器获取token:"+result);  
  26.             JSONObject rqJsonObject = JSONObject.fromObject(result);  
  27.             TokenJson tokenJson = (TokenJson) JSONObject.toBean(rqJsonObject,TokenJson.class);  
  28.             return tokenJson;  
  29.         } catch (Exception e) {  
  30.             // TODO Auto-generated catch block  
  31.             e.printStackTrace();  
  32.             return null;  
  33.         }  
  34.     }  
  35.       
  36.       
  37.     private static TicketJson getTicket(String token){  
  38.         String url = String.format("http://api.weixin.qq.com/xxxxxxxxxxxxxxxxxxxxxxxx",token);  
  39.         try {  
  40.             String result = HttpGetRequest.doGet(url);  
  41.             System.out.println("微信服务器获取Ticket:"+result);  
  42.             JSONObject rqJsonObject = JSONObject.fromObject(result);  
  43.             TicketJson ticket = (TicketJson) JSONObject.toBean(rqJsonObject,TicketJson.class);  
  44.             return ticket;  
  45.         } catch (Exception e) {  
  46.             // TODO Auto-generated catch block  
  47.             e.printStackTrace();  
  48.             return null;  
  49.         }  
  50.     }  
  51.       
  52.     /** 
  53.      * 获取js sdk 认证信息 
  54.     * @author  
  55.     * @date 创建时间 2016年7月28日 上午11:25:01  
  56.     * @param url 
  57.     * @return 
  58.      */  
  59.     public static Map<String, String> getSign(String url){  
  60.           
  61.         //处理token失效的问题  
  62.         try {  
  63.             long tokenTimeLong = Long.parseLong(WxParams.tokenTime);  
  64.             long tokenExpiresLong = Long.parseLong(WxParams.tokenExpires);  
  65.               
  66.             //时间差  
  67.             long differ = (System.currentTimeMillis() - tokenTimeLong) /1000;  
  68.             if (WxParams.token == null ||  differ > (tokenExpiresLong - 1800)) {  
  69.                 System.out.println("token为null,或者超时,重新获取");  
  70.                 TokenJson tokenJson = getAccess_token();  
  71.                 if (tokenJson != null) {  
  72.                     WxParams.token = tokenJson.getAccess_token();  
  73.                     WxParams.tokenTime = System.currentTimeMillis()+"";  
  74.                     WxParams.tokenExpires = tokenJson.getExpires_in()+"";  
  75.                 }  
  76.             }  
  77.         } catch (Exception e) {  
  78.             // TODO: handle exception  
  79.             e.printStackTrace();  
  80.             TokenJson tokenJson = getAccess_token();  
  81.             if (tokenJson != null) {  
  82.                 WxParams.token = tokenJson.getAccess_token();  
  83.                 WxParams.tokenTime = System.currentTimeMillis()+"";  
  84.                 WxParams.tokenExpires = tokenJson.getExpires_in()+"";  
  85.             }  
  86.         }  
  87.   
  88.         //处理ticket失效的问题  
  89.         try {  
  90.             long ticketTimeLong = Long.parseLong(WxParams.ticketTime);  
  91.             long ticketExpiresLong = Long.parseLong(WxParams.ticketExpires);  
  92.               
  93.             //时间差  
  94.             long differ = (System.currentTimeMillis() - ticketTimeLong) /1000;  
  95.             if (WxParams.ticket == null ||  differ > (ticketExpiresLong - 1800)) {  
  96.                 System.out.println("ticket为null,或者超时,重新获取");  
  97.                 TicketJson ticketJson = getTicket(WxParams.token);  
  98.                 if (ticketJson != null) {  
  99.                     WxParams.ticket = ticketJson.getTicket();  
  100.                     WxParams.ticketTime = System.currentTimeMillis()+"";  
  101.                     WxParams.ticketExpires = ticketJson.getExpires_in()+"";  
  102.                 }  
  103.             }  
  104.         } catch (Exception e) {  
  105.             // TODO: handle exception  
  106.             e.printStackTrace();  
  107.             TicketJson ticketJson = getTicket(WxParams.token);  
  108.             if (ticketJson != null) {  
  109.                 WxParams.ticket = ticketJson.getTicket();  
  110.                 WxParams.ticketTime = System.currentTimeMillis()+"";  
  111.                 WxParams.ticketExpires = ticketJson.getExpires_in()+"";  
  112.             }  
  113.         }  
  114.   
  115.         Map<String, String> ret = Sign.sign(WxParams.ticket, url);  
  116.         System.out.println("计算出的签名-----------------------");  
  117.         for (Map.Entry entry : ret.entrySet()) {  
  118.             System.out.println(entry.getKey() + ", " + entry.getValue());  
  119.         }  
  120.         System.out.println("-----------------------");  
  121.         return ret;  
  122.     }  
  123.   
  124. }  
整个后台代码,全献上了,复制粘贴更改关键部分即可使用,不管你们能不能用,反正我是可以用~ 2333333333。
index.jsp  通过<%%> 嵌入
[java] view plain copy
  1. <%  
  2. //签名  
  3. String url = request.getScheme()+"://";  
  4. url+=request.getHeader("host");     
  5. url+=request.getRequestURI();     
  6. if(request.getQueryString()!=null){  
  7.     url+="?"+request.getQueryString();     
  8. }  
  9.   
  10. Map<String,String> sign = WxUtil.getSign(url);  
  11. String timestamp = sign.get("timestamp");  
  12. String nonceStr = sign.get("nonceStr");  
  13. String jsapi_ticket = sign.get("jsapi_ticket");  
  14. String signature = sign.get("signature");  
  15. //String url = sign.get("url");  
  16.   
  17. %>  
拿到后台反馈的信息后,使用wx.config({}) ;进行初始化操作如下:
[javascript] view plain copy
  1. <script type="text/javascript" src="jweixin-1.0.0.js"></script>  
  2. <script type="text/javascript">  
  3.   
  4. wx.config({  
  5.     debug: false,  
  6.     appId: <%="'"+WxUtil.APPID+"'"%>,  
  7.     timestamp: <%="'"+timestamp+"'"%>,  
  8.     nonceStr: <%="'"+nonceStr+"'"%>,  
  9.     signature: <%="'"+signature+"'"%>,  
  10.     jsApiList: ['onMenuShareTimeline''onMenuShareAppMessage''onMenuShareQQ''onMenuShareWeibo''onMenuShareQZone'// 功能列表,我们要使用JS-SDK的什么功能  
  11. });  
  12.   
  13.   
  14. // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在 页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready 函数中。  
  15. wx.ready(function(){  
  16.     // 获取"分享到朋友圈"按钮点击状态及自定义分享内容接口  
  17.     wx.onMenuShareTimeline({  
  18.         title: '慧锐通电子书架'// 分享标题  
  19.         link: 'http://www.wrtrd.net/book/',  
  20.         imgUrl: 'http://www.wrtrd.net/book/images/wxbook.jpg' // 分享图标  
  21.     });  
  22.       
  23.       
  24.     // 获取"分享给朋友"按钮点击状态及自定义分享内容接口  
  25.     wx.onMenuShareAppMessage({  
  26.         title: '慧锐通电子书架'// 分享标题  
  27.         desc: '慧锐通产品介绍的电子画册,含数字对讲、模拟对讲、云对讲、智能互联、蓝牙门禁等系统!'// 分享描述  
  28.         link: 'http://www.wrtrd.net/book/',  
  29.         imgUrl: 'http://www.wrtrd.net/book/images/wxbook.jpg', // 分享图标  
  30.         type: 'link' // 分享类型,music、video或link,不填默认为link  
  31.     });  
  32.       
  33.       
  34.     //获取"分享到QQ"按钮点击状态及自定义分享内容接口  
  35.     wx.onMenuShareQQ({  
  36.     title: '慧锐通电子书架'// 分享标题  
  37.     desc: '慧锐通产品介绍的电子画册,含数字对讲、模拟对讲、云对讲、智能互联、蓝牙门禁等系统!'// 分享描述  
  38.     link: 'http://www.wrtrd.net/book/', // 分享链接  
  39.     imgUrl: 'http://www.wrtrd.net/book/images/wxbook.jpg' // 分享图标  
  40.     });  
  41.       
  42.       
  43.     //获取"分享到腾讯微博"按钮点击状态及自定义分享内容接口  
  44.     wx.onMenuShareWeibo({  
  45.     title: '慧锐通电子书架'// 分享标题  
  46.     desc: '慧锐通产品介绍的电子画册,含数字对讲、模拟对讲、云对讲、智能互联、蓝牙门禁等系统!'// 分享描述  
  47.     link: 'http://www.wrtrd.net/book/', // 分享链接  
  48.     imgUrl: 'http://www.wrtrd.net/book/images/wxbook.jpg' // 分享图标  
  49.     });  
  50.       
  51.       
  52.     //获取"分享到QQ空间"按钮点击状态及自定义分享内容接口  
  53.     wx.onMenuShareQZone({  
  54.     title: '慧锐通电子书架'// 分享标题  
  55.     desc: '慧锐通产品介绍的电子画册,含数字对讲、模拟对讲、云对讲、智能互联、蓝牙门禁等系统!'// 分享描述  
  56.     link: 'http://www.wrtrd.net/book/', // 分享链接  
  57.     imgUrl: 'http://www.wrtrd.net/book/images/wxbook.jpg' // 分享图标  
  58.     });  
  59.       
  60. });  
  61.   
  62. </script>  
以上是全部代码,基本稍加更改则可直接使用。

其中,wx.config({})是整体的关键,在页面初始化时会自动的验证后台信息,验证成功会自动调用wx.ready(function(){ }
[javascript] view plain copy
  1. wx.ready(function(){  
  2.   
  3.     // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。  
  4. });  
验证失败调用
[java] view plain copy
  1. wx.error(function(res){  
  2.   
  3.     // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。  
  4.   
  5. });  
使用注意事项看这里:看这里看这里 。 补充两点,如果以上代码都编辑后,测试时发现 wx.config验证通过,基本上按照JS-SDK开发文档来就足够了。同理,如果wx.config验证不通过,请直接百度可能出现的原因,有可能是提供的开发者账号等错误,并有一个隐藏的错误,传入后台的URL与当前页面URL不匹配,此时也不会通过 ~~~ T~T 被这个原因坑了一整天,
同时,测试时,必须部署到微信公众平台账号中,因为它会检测当前传入的HTTP地址是否与备案的相同,如若不同则会一直报异常,切记切记。。。
阅读全文
0 0
原创粉丝点击