20180811写在前面的话

有很多人遇到问题之后问我,结果大多数是因为配置问题,所以请详细阅读前面的配置步骤。

20181016注意事项

收到反馈,之前写的接口即将废弃,源代码中的js接口需要修改,详情请参考https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115中的分享接口,需要修改接口名称和参数列表。

文中源码下载地址

https://download.csdn.net/download/shrmuscles/10611245

上面这个地址是我当时做的整个项目的源代码,包含了一些业务在里面,用的是SSM框架,现在写了一个SpringBoot版本的,只有页面分享的代码,不包含业务的代码,简单清晰。另外,在官方文档中看到,以前写的分享接口即将废弃,并且给上了新的接口,代码中的接口用的是以前的接口,所以下载之后建议修改成新的接口

20200408看到20200401有人给我发私信说上面的代码没有数据库脚本,非常抱歉,因为我当时想的是最重要的是实现分享,没有考虑到有人要把项目跑起来,所以重建了一下数据库脚本,目的是让项目跑起来,分享和项目中的业务无关。

下载了资源的自取脚本:https://github.com/ShrMus/note/blob/master/knowledge/kavo_database_script/kavo.sql

SpringBoot版本下载地址

https://download.csdn.net/download/shrmuscles/10725021

正文

我这几天做的是自定义分享到朋友圈和分享给好友,能够自己设置分享出来的标题,描述和图片。

自定义分享到朋友圈需要调用微信的JS接口,个人公众号和企业公众号的拥有的接口权限都不一样。企业认证过的公众号拥有的接口权限会多一些。

那需要怎么做呢,官方文档的【微信JS-SDK说明文档】中写了JSSDK的使用步骤。

官方文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115

第一步

绑定域名,在【公众号设置】的【功能设置】中设置JS接口安全域名和网页授权域名,做这两步都要下载一个txt文件。

我用Java写后台,所以Http服务器用的是Tomcat,这里要注意的是,如果你的域名是直接指向到你的服务器,需要把Tomcat的默认端口改成80,然后把这个文件放在webapps/ROOT目录下,如果输入域名/文件名在浏览器能够看到文件的内容就说明文件的位置放正确了,点保存就好了。

第二步

引入JS文件(支持https):http://res.wx.qq.com/open/js/jweixin-1.2.0.js

第三步

通过config接口注入权限验证配置

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

这个时候我蒙圈了,这什么玩意,这些参数是自己输吗?在哪获得这些参数呢。于是我在公众号里努力找也只找到了appID,其他的参数怎么获得?timestamp和nonceStr还好解决,signature怎么获得,注释里写见附录1。

于是我翻到附录1

这里写到生成签名先要了解jsapi_ticket,而jsapi_ticket是通过access_token来获取的。

接下来看看access_token是什么,官方文档中【开始开发】中【获取access_token】中有说明,公众号可以使用AppID和AppSecret调用本接口来获取access_token。

AppID在微信公众号的【开发】的【基本配置】有,【基本配置】中也有AppSecret,获取就行了,真是得来全不费功夫,但是要用文档记下AppID和AppSecret,获取access_token会用到。

这里还有个IP白名单,只有将IP地址设置为公众号的IP白名单,才能成功调用该接口。AppID和AppSecret可在“微信公众平台-开发-基本配置”页中获得(需要已经成为开发者,且帐号没有异常状态)。调用接口时,请登录“微信公众平台-开发-基本配置”提前将服务器IP地址添加到IP白名单中,点击查看设置方法,否则将无法调用成功。可以设置多个,输入一个IP地址之后回车就行了,我遇到过几次情况就是在公司那个地方的公网IP和我住的地方的公网IP不一样,导致我在家的时候出现了后面的access_token获取不到的情况。记得要配置线上环境的IP地址。

现在AppID和AppSecret都有了,怎么获得access_token。前面看的附录1中写了全局缓存jsapi_ticket和access_token,官方文档【获取access_token】中也有获得access_token的URL。

https请求方式: GET

https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

定义一个类AccessToken

public class AccessToken implements java.io.Serializable {// 接口访问凭证private String accessToken;// 凭证有效期,单位:秒private int expiresIn;public AccessToken() {}public String getAccessToken() {return accessToken;}public void setAccessToken(String accessToken) {this.accessToken = accessToken;}public int getExpiresIn() {return expiresIn;}public void setExpiresIn(int expiresIn) {this.expiresIn = expiresIn;}
}

再写一个类JsApiTicket

public class JsApiTicket implements java.io.Serializable {private String ticket;// 凭证有效期,单位:秒private int expiresIn;public JsApiTicket() {}public String getTicket() {return ticket;}public void setTicket(String ticket) {this.ticket = ticket;}public int getExpiresIn() {return expiresIn;}public void setExpiresIn(int expiresIn) {this.expiresIn = expiresIn;}}

线上环境需要项目发布到服务器上就能开始获取access_token,创建一个InitAccessTokenServlet,Tomcat启动就初始化这个Servlet。

创建一个InitAccessTokenServlet

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;import com.kavo.utils.AccessTokenThread;
import com.kavo.utils.JsApiTicketThread;public class InitAccessTokenServlet extends HttpServlet {public void init() throws ServletException {// 获取web.xml中配置的参数String WX_APPID = getInitParameter("appid");String WX_APPSECRET = getInitParameter("appsecret");AccessTokenThread.appid = WX_APPID;AccessTokenThread.appsecret = WX_APPSECRET;if ("".equals(AccessTokenThread.appid) || "".equals(AccessTokenThread.appsecret)) {System.out.println("appid和appsecret未给出");} else {new Thread(new AccessTokenThread()).start();new Thread(new JsApiTicketThread()).start();}}}

代码中有getInitParameter方法,这是获取初始值的方法,在web.xml中需要这样配置。

<servlet><servlet-name>InitAccessTokenServlet</servlet-name><servlet-class>com.kavo.controller.InitAccessTokenServlet</servlet-class><init-param><param-name>appid</param-name><param-value>公众号appid</param-value></init-param><init-param><param-name>appsecret</param-name><param-value>公众号addsecret</param-value></init-param><load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping><servlet-name>InitAccessTokenServlet</servlet-name><url-pattern>/InitAccessTokenServlet</url-pattern>
</servlet-mapping>

在InitAccessTokenServlet中有一段代码是开启线程的。因为access_token的有效期是2小时(看文档),所以获取一次之后2小时之内需要再获取。

下面是获取access_token的线程

创建一个AccessTokenThread

import javax.servlet.ServletContext;import com.kavo.pojo.AccessToken;public class AccessTokenThread implements Runnable {public static String appid = "";public static String appsecret = "";public static AccessToken accessToken = null;@Overridepublic void run() {while (true) {try {accessToken = CommonUtil.getAccessToken(appid, appsecret);if (null != accessToken) {System.out.println("accessToken初始化成功:" + accessToken.getAccessToken());// 全局缓存access_tokenServletContext servletContext = ServletContextUtil.getServletContext();servletContext.setAttribute("access_token", accessToken.getAccessToken());// 有效期(秒)减去200秒,乘以1000(毫秒)——也就是在有效期的200秒前去请求新的accessTokenThread.sleep((accessToken.getExpiresIn() - 200) * 1000);} else {// 等待一分钟,再次请求Thread.sleep(60 * 1000);}} catch (Exception e) {try {// 等待一分钟,再次请求Thread.sleep(60 * 1000);} catch (Exception ex) {ex.printStackTrace();}e.printStackTrace();}}}
}

然后是获取jsapi_ticket的线程

创建一个JSApiTicketThread

import javax.servlet.ServletContext;import com.kavo.pojo.JsApiTicket;public class JsApiTicketThread implements Runnable {@Overridepublic void run() {while (true) {try {ServletContext servletContext = ServletContextUtil.getServletContext();String access_token = (String) servletContext.getAttribute("access_token");JsApiTicket jsApiTicket = null;if(null != access_token && !"".equals(access_token)){// 获取jsapi_ticketjsApiTicket = CommonUtil.getJsApiTicket(access_token);if (null != jsApiTicket) {System.out.println("jsapi_ticket获取成功:" + jsApiTicket.getTicket());// 全局缓存jsapi_ticketservletContext.setAttribute("jsapi_ticket", jsApiTicket.getTicket());Thread.sleep((jsApiTicket.getExpiresIn() - 200) * 1000);}}Thread.sleep(60 * 1000);} catch (Exception e) {try {Thread.sleep(60 * 1000);} catch (Exception ex) {ex.printStackTrace();}e.printStackTrace();}}}}

这两个线程都用了一个公共类CommonUtil用来获取access_token和jsapi_ticket

创建一个公共类CommonUtil

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kavo.pojo.AccessToken;
import com.kavo.pojo.JsApiTicket;public class CommonUtil {// 凭证获取(GET)——access_tokenpublic final static String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";// 微信JSSDK的ticket请求URL地址——jsapi_ticketpublic final static String JSAPI_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";/*** 发送https请求* * @param requestUrl*            请求地址* @param requestMethod*            请求方式(GET、POST)* @param outputStr*            提交的数据* @return rootNode(通过rootNode.get(key)的方式获取json对象的属性值)*/public static JsonNode httpsRequest(String requestUrl, String requestMethod, String outputStr) {ObjectMapper mapper = new ObjectMapper();JsonNode rootNode = null;StringBuffer buffer = new StringBuffer();try {// 创建SSLContext对象,并使用我们指定的信任管理器初始化TrustManager[] tm = { new MyX509TrustManager() };SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");sslContext.init(null, tm, new java.security.SecureRandom());// 从上述SSLContext对象中得到SSLSocketFactory对象SSLSocketFactory ssf = sslContext.getSocketFactory();URL url = new URL(requestUrl);HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();conn.setSSLSocketFactory(ssf);conn.setDoOutput(true);conn.setDoInput(true);conn.setUseCaches(false);// 设置请求方式(GET/POST)conn.setRequestMethod(requestMethod);//conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");if ("GET".equalsIgnoreCase(requestMethod))conn.connect();// 当outputStr不为null时向输出流写数据if (null != outputStr) {OutputStream outputStream = conn.getOutputStream();// 注意编码格式outputStream.write(outputStr.getBytes("UTF-8"));outputStream.close();}// 从输入流读取返回内容InputStream inputStream = conn.getInputStream();InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");BufferedReader bufferedReader = new BufferedReader(inputStreamReader);String str = null;while ((str = bufferedReader.readLine()) != null) {buffer.append(str);}// 释放资源bufferedReader.close();inputStreamReader.close();inputStream.close();inputStream = null;conn.disconnect();rootNode = mapper.readTree(buffer.toString());} catch (Exception e) {e.printStackTrace();}return rootNode;}/*** 获取接口访问凭证* * @param appid*            凭证* @param appsecret*            密钥* @return*/public static AccessToken getAccessToken(String appid, String appsecret) {AccessToken accessToken = null;String requestUrl = ACCESS_TOKEN_URL.replace("APPID", appid).replace("APPSECRET", appsecret);// 发起GET请求获取凭证JsonNode rootNode = httpsRequest(requestUrl, "GET", null);if (null != rootNode.get("access_token")) {accessToken = new AccessToken();accessToken.setAccessToken(rootNode.get("access_token").textValue());accessToken.setExpiresIn(toInt(rootNode.get("expires_in").toString()));}return accessToken;}/*** 调用微信JS接口的临时票据* * @param access_token*            接口访问凭证* @return*/public static JsApiTicket getJsApiTicket(String access_token) {String requestUrl = JSAPI_TICKET_URL.replace("ACCESS_TOKEN", access_token);// 发起GET请求获取凭证JsonNode rootNode = httpsRequest(requestUrl, "GET", null);JsApiTicket jsApiTicket = null;if (null != rootNode.get("ticket")) {jsApiTicket = new JsApiTicket();jsApiTicket.setTicket(rootNode.get("ticket").textValue());jsApiTicket.setExpiresIn(toInt(rootNode.get("expires_in").toString()));}return jsApiTicket;}public static Integer toInt(String str) {if (str == null || str.equals("")) {return null;}return Integer.valueOf(str);}}

这个公共类中用到了证书信任管理器类MyX509TrustManager

创建MyX509TrustManager

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;import javax.net.ssl.X509TrustManager;public class MyX509TrustManager implements X509TrustManager {// 检查客户端证书@Overridepublic void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}// 检查服务器端证书@Overridepublic void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}// 返回受信任的X509证书数组@Overridepublic X509Certificate[] getAcceptedIssuers() {return null;}}

线程中还用到了一个获取全局缓存的工具类ServletContextUtil

创建ServletContextUtil

import javax.servlet.ServletContext;import org.springframework.stereotype.Component;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;@Component
public final class ServletContextUtil {private static ServletContext serveltContext = null;  private ServletContextUtil(){};  public synchronized static ServletContext getServletContext() {  if(null == serveltContext) {  WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();    serveltContext = webApplicationContext.getServletContext();   }   return serveltContext;  }
}

到这里为止,获取access_token和获取jsapi_ticket就写好了,公共类中CommonUtil的httpsRequest()方法可能还有不同的写法,大家可以到网上再了解。

关于获取jsapi_ticket,在官方文档【微信JS-SDK说明文档】的【附录1】中有详细说明。

请一定详细阅读【附录1】,这里讲了如何获得签名,代码在【附录6-DEMO页面和示例代码】,URL是http://demo.open.weixin.qq.com/jssdk/sample.zip,里面有Java代码写的如何获取签名。

我用的是SSM框架,把这个类改成了Controller。

创建InitAccessTokenController

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;import javax.servlet.ServletContext;
import javax.servlet.http.HttpServlet;import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import com.kavo.utils.ServletContextUtil;@Controller
@Scope("prototype")
public class InitAccessTokenController extends HttpServlet{@Value("${WX_APPID}")private String WX_APPID;@RequestMapping("/initWXJSInterface")public @ResponseBody Map<String, String> init(String url){// 从全局缓存中取出jsapi_ticketServletContext servletContext = ServletContextUtil.getServletContext();String jsapi_ticket = (String) servletContext.getAttribute("jsapi_ticket");Map<String, String> ret = sign(jsapi_ticket, url);System.out.println("currurl = "+ url);// 注意 URL 一定要动态获取,不能 hardcode
//      for (Map.Entry entry : ret.entrySet()) {
//          System.out.println(entry.getKey() + ", " + entry.getValue());
//      }System.out.println("signature =" + ret.get("signature"));return ret;}public Map<String, String> sign(String jsapi_ticket, String url) {Map<String, String> ret = new HashMap<String, String>();String nonce_str = create_nonce_str();String timestamp = create_timestamp();String string1;String signature = "";// 注意这里参数名必须全部小写,且必须有序string1 = "jsapi_ticket=" + jsapi_ticket +"&noncestr=" + nonce_str +"&timestamp=" + timestamp +"&url=" + url;System.out.println(string1);try {MessageDigest crypt = MessageDigest.getInstance("SHA-1");crypt.reset();crypt.update(string1.getBytes("UTF-8"));signature = byteToHex(crypt.digest());} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (UnsupportedEncodingException e) {e.printStackTrace();}ret.put("url", url);ret.put("appId",WX_APPID);ret.put("jsapi_ticket", jsapi_ticket);ret.put("nonceStr", nonce_str);ret.put("timestamp", timestamp);ret.put("signature", signature);return ret;}private static String byteToHex(final byte[] hash) {Formatter formatter = new Formatter();for (byte b : hash) {formatter.format("%02x", b);}String result = formatter.toString();formatter.close();return result;}private static String create_nonce_str() {return UUID.randomUUID().toString();}private static String create_timestamp() {return Long.toString(System.currentTimeMillis() / 1000);}}

字段WX_APPID是写在配置文件中的,新建一个config.properties,在springmvc的配置文件中加载这个文件。因为步骤三中需要appid这个参数。在sign方法中要记得把WX_APPID添加到ret中。

这个时候再看步骤三,这些参数都有了,jsApiList可以看附录

第四步

然后把分享到朋友圈的这段代码写到wx.ready中,自己设置参数就行了,imgUrl要填写的是图片的url

再啰嗦一句,如果wx.config中的debug参数为true,每次调用都会弹出调用的信息,详细请看【微信JS-SDK说明文档】中的接口调用说明。下面是前台页面的代码:

<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
<script type="text/javascript">
$(document).ready(function() {var currurl = decodeURIComponent(location.href.split('#')[0]);//ajax注入权限验证  $.ajax({url : "initWXJSInterface",dataType : 'json',data : {'url' : currurl},complete : function(XMLHttpRequest, textStatus) {},error : function(XMLHttpRequest, textStatus, errorThrown) {alert("发生错误:" + errorThrown);},success : function(res) {var appId = res.appId;var nonceStr = res.nonceStr;var jsapi_ticket = res.jsapi_ticket;var timestamp = res.timestamp;var signature = res.signature;// alert(appId +" " + nonceStr +" "+jsapi_ticket+" "+timestamp+" "+signature);wx.config({debug : false, //开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。  appId : appId, //必填,公众号的唯一标识  timestamp : timestamp, // 必填,生成签名的时间戳  nonceStr : nonceStr, //必填,生成签名的随机串  signature : signature, // 必填,签名,见附录1  jsApiList : [ 'onMenuShareAppMessage', 'onMenuShareTimeline' ] //必填,需要使用的JS接口列表,所有JS接口列表 见附录2  }); // end wx.configwx.ready(function() {// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。/* wx.checkJsApi({jsApiList : [ 'onMenuShareAppMessage' ], // 需要检测的JS接口列表,所有JS接口列表见附录2,success : function(res) {// 以键值对的形式返回,可用的api值true,不可用为false// 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}alert(res.checkResult);alert(res.errMsg);}}); // end checkJsApi*/wx.onMenuShareAppMessage({title : '分享好友标题', // 分享标题desc : '分享好友描述', // 分享描述link : currurl, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致imgUrl : 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1505419265109&di=cc30743d364e5ae89172c62a662e1321&imgtype=0&src=http%3A%2F%2Fpic.qqtn.com%2Fup%2F2017-6%2F14973136731543515.jpg', // 分享图标type : '', // 分享类型,music、video或link,不填默认为linkdataUrl : '', // 如果type是music或video,则要提供数据链接,默认为空success : function() {// 用户确认分享后执行的回调函数// alert('share successful');},cancel : function() {// 用户取消分享后执行的回调函数// alert('share cancel');}}); // end onMenuShareAppMessagewx.onMenuShareTimeline({title : '分享朋友圈标题', // 分享标题link : currurl, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致imgUrl : 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1505419265109&di=cc30743d364e5ae89172c62a662e1321&imgtype=0&src=http%3A%2F%2Fpic.qqtn.com%2Fup%2F2017-6%2F14973136731543515.jpg', // 分享图标success : function() {// 用户确认分享后执行的回调函数},cancel : function() {// 用户取消分享后执行的回调函数}}); // end onMenuShareTimeline}); // end readywx.error(function(res) {// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。alert('error');});} // end success}); // end ajax});  // end document</script>

把项目发布到服务器上,然后手机访问,分享出来的就是上面这段js里写的标题,描述和图片了。

总结

  • 微信公众号里面配置JS接口安全域名,网页授权域名,IP白名单
  • 分享的页面中引入微信JS文件
  • AppID和AppSecret在微信公众号获取
  • 通过AppID和AppSecret获取access_token
  • 通过access_token获取jsapi_ticket
  • 在分享的页面中获取后台的appid,jsapi_ticket等数据
  • 在分享的页面中的JavaScript代码通过wx.config接口注入权限验证配置,需要后台的appid,jsapi_ticket等数据
  • 在wx.config接口之后通过wx.ready处理成功验证
  • 把调用的JS接口写在wx.config中

20180606补充:

1、前台的代码添加到需要自定义分享的页面中。

2、appid,jsapi_ticket,signature等数据需要在页面中加载时请求后台获取,不能在页面中自己定义赋值。

3、invalid url domain错误:

  • 检查当前页面所在域名和JS接口安全域名是否一致
  • 检查AppID和公众号后台的AppID是否一致
  • JS接口安全域名配置一级域名

微信公众号开发 自定义分享 从前台到Java后台 调用微信JS接口分享朋友圈相关推荐

  1. java自定义菜单跳转页面_微信公众号开发 自定义菜单跳转页面并获取用户信息实例详解...

    微信公众号开发 自定义菜单 请先读完本文再进行配置开发 请先前往微信平台开发者文档阅读"网页授权获取用户基本信息"的接口说明 在微信公众账号开发中,往往有定义一个菜单,然后用户点击 ...

  2. 微信公众号开发--自定义菜单跳转页面并获取用户信息(续)

    之前写过一篇微信公众号开发–自定义菜单跳转页面并获取用户信息 由于当时是刚学习微信公众号开发当时的思路虽然可行,不过不是最好的,最近也用到了需要获取用户信息的地方,再次整理一下. 流程 注意点 ### ...

  3. 公众号如何跳转到页面php,图文详解微信公众号开发自定义菜单跳转页面并获取用户信息实例...

    这篇文章主要介绍了微信公众号开发 自定义菜单跳转页面并获取用户信息实例详解的相关资料,需要的朋友可以参考下 微信公众号开发 自定义菜单 请先读完本文再进行配置开发 请先前往微信平台开发者文档阅读&qu ...

  4. 微信公众号开发系列-13、基于RDIFramework.NET框架整合微信开发应用效果展示

    微信公众号开发系列-13.基于RDIFramework.NET框架整合微信开发应用效果展示 1.前言 通过前面一系列文章的学习,我们对微信公众号开发已经有了一个比较深入和全面的了解. 微信公众号开发为 ...

  5. 微信公众号开发之服务器接入指南之Java版本

    微信公众号开发的官方文档: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319 其中我们开发好的服务器,在配置到 ...

  6. 微信公众号开发--双色球彩票开奖结果(Java版)

    可扫描下方二维码关注 你我杂志刊 或微信搜索公众号 你我杂志刊 然而请求这个接口似乎有点慢了.在PC端的浏览器请求也并不是很快! 这次主要使用了免费的 开彩API http://www.opencai ...

  7. 微信公众号开发--接收与回复消息(Java)

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 第一步: ...

  8. 微信公众号开发---自定义菜单的创建及菜单事件响应(java)

    微信5.0发布 2013年8月5日,伴随着微信5.0 iPhone版的发布,公众平台也进行了重要的更新,主要包括: 1)运营主体为组织,可选择成为服务号或者订阅号: 2)服务号可以申请自定义菜单: 3 ...

  9. WxJava微信公众号开发实战

    本文从本人博客搬运,原文格式更加美观,可以移步原文阅读:WxJava微信公众号开发实战 之前我们介绍了Java如何进行微信公众号开发,阅读本文前小伙伴们可以先去了解下Java微信公众号开发 之前我们开 ...

最新文章

  1. Strategy_Level1
  2. Python_note5 函数和代码复用+PyInstaller库+数码管绘制
  3. XML转JSON的javascript代码
  4. Asp.net MVC防止图片盗链的实现方法,通过自定义RouteHandler来操作
  5. android后台如何动态添加图片,android – 如何从JSON数组动态地向ImageView添加图像...
  6. js 异步操作打开新页面被浏览器拦截的问题
  7. 聊聊风口上的 eBPF
  8. 深度学习在轨迹数据挖掘中的应用研究综述
  9. 表格c1等于a1加b1_中国最牛驾照,C1、B1、B2、A1、A2随便开,被称为万能的驾照!...
  10. SAP License:SAP电话面试
  11. javascript JSON.parse和eval的区别
  12. 直接安装的Qt 5如何调试到源码中
  13. 【安全牛学习笔记】SSL、TLS拒绝服务***和补充概念
  14. IP-SAN在Windows端配置
  15. [CF438D]The Child and Sequence
  16. exls表格搜索快捷键_excel表格快速查找快捷键
  17. 针对Win10 Tensorflow-2.x 训练 线程冻结(假死、卡住、几个epoch后、中途停滞、CPU占用0%) 等问题的排查总结
  18. 对接支付宝、微信、第三方支付,超详细讲解+demo演示
  19. 【AI绘画】二次元小姐姐生成!春节版!
  20. 争对让望对思野葛对山栀注解_解析

热门文章

  1. kaggle网站注册登录流程详细介绍(小白必看)
  2. 计算器(可随意编辑)
  3. X-Pack:创建阈值检查警报
  4. HTML特殊转义字符
  5. 对图片进行锐化处理,通俗易懂!
  6. word文件自动变成只读模式,怎么办?
  7. 交换机链路聚合(静态,动态)
  8. 计算机信息安全技术学习资料汇总
  9. 电子商务业务模块分析和介绍以及开发流程
  10. JavaScript typeof, null, 和 undefined