zoukankan      html  css  js  c++  java
  • 第六篇 :微信公众平台开发实战Java版之如何自定义微信公众号菜单

    我们来了解一下

    自定义菜单创建接口:

    http请求方式:POST(请使用https协议) https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN

    自定义菜单查询接口:

    http请求方式:GET https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN

    自定义菜单删除接口:

    http请求方式:GET https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN

    自定义菜单接口可实现多种类型按钮,如下:

    1、click:点击推事件
    用户点击click类型按钮后,微信服务器会通过消息接口推送消息类型为event    的结构给开发者(参考消息接口指南),并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互;
    2、view:跳转URL
    用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的网页URL,可与网页授权获取用户基本信息接口结合,获得用户基本信息。
    3、scancode_push:扫码推事件
    用户点击按钮后,微信客户端将调起扫一扫工具,完成扫码操作后显示扫描结果(如果是URL,将进入URL),且会将扫码的结果传给开发者,开发者可以下发消息。
    4、scancode_waitmsg:扫码推事件且弹出“消息接收中”提示框
    用户点击按钮后,微信客户端将调起扫一扫工具,完成扫码操作后,将扫码的结果传给开发者,同时收起扫一扫工具,然后弹出“消息接收中”提示框,随后可能会收到开发者下发的消息。
    5、pic_sysphoto:弹出系统拍照发图
    用户点击按钮后,微信客户端将调起系统相机,完成拍照操作后,会将拍摄的相片发送给开发者,并推送事件给开发者,同时收起系统相机,随后可能会收到开发者下发的消息。
    6、pic_photo_or_album:弹出拍照或者相册发图
    用户点击按钮后,微信客户端将弹出选择器供用户选择“拍照”或者“从手机相册选择”。用户选择后即走其他两种流程。
    7、pic_weixin:弹出微信相册发图器
    用户点击按钮后,微信客户端将调起微信相册,完成选择操作后,将选择的相片发送给开发者的服务器,并推送事件给开发者,同时收起相册,随后可能会收到开发者下发的消息。
    8、location_select:弹出地理位置选择器
    用户点击按钮后,微信客户端将调起地理位置选择工具,完成选择操作后,将选择的地理位置发送给开发者的服务器,同时收起位置选择工具,随后可能会收到开发者下发的消息。
    9、media_id:下发消息(除文本消息)
    用户点击media_id类型按钮后,微信服务器会将开发者填写的永久素材id对应的素材下发给用户,永久素材类型可以是图片、音频、视频、图文消息。请注意:永久素材id必须是在“素材管理/新增永久素材”接口上传后获得的合法id。
    10、view_limited:跳转图文消息URL
    用户点击view_limited类型按钮后,微信客户端将打开开发者在按钮中填写的永久素材id对应的图文消息URL,永久素材类型只支持图文消息。请注意:永久素材id必须是在“素材管理/新增永久素材”接口上传后获得的合法id。
    View Code

    请注意,3到8的所有事件,仅支持微信iPhone5.4.1以上版本,和Android5.4以上版本的微信用户,旧版本微信用户点击后将没有回 应,开发者也不能正常接收到事件推送。9和10,是专门给第三方平台旗下未微信认证(具体而言,是资质认证未通过)的订阅号准备的事件类型,它们是没有事 件推送的,能力相对受限,其他类型的公众号不必使用。

    click和view的请求示例

     {
         "button":[
         {    
              "type":"click",
              "name":"今日歌曲",
              "key":"V1001_TODAY_MUSIC"
          },
          {
               "name":"菜单",
               "sub_button":[
               {    
                   "type":"view",
                   "name":"搜索",
                   "url":"http://www.soso.com/"
                },
                {
                   "type":"view",
                   "name":"视频",
                   "url":"http://v.qq.com/"
                },
                {
                   "type":"click",
                   "name":"赞一下我们",
                   "key":"V1001_GOOD"
                }]
           }]
     }
    View Code
    
    

    其他新增按钮类型的请求示例

    {
        "button": [
            {
                "name": "扫码", 
                "sub_button": [
                    {
                        "type": "scancode_waitmsg", 
                        "name": "扫码带提示", 
                        "key": "rselfmenu_0_0", 
                        "sub_button": [ ]
                    }, 
                    {
                        "type": "scancode_push", 
                        "name": "扫码推事件", 
                        "key": "rselfmenu_0_1", 
                        "sub_button": [ ]
                    }
                ]
            }, 
            {
                "name": "发图", 
                "sub_button": [
                    {
                        "type": "pic_sysphoto", 
                        "name": "系统拍照发图", 
                        "key": "rselfmenu_1_0", 
                       "sub_button": [ ]
                     }, 
                    {
                        "type": "pic_photo_or_album", 
                        "name": "拍照或者相册发图", 
                        "key": "rselfmenu_1_1", 
                        "sub_button": [ ]
                    }, 
                    {
                        "type": "pic_weixin", 
                        "name": "微信相册发图", 
                        "key": "rselfmenu_1_2", 
                        "sub_button": [ ]
                    }
                ]
            }, 
            {
                "name": "发送位置", 
                "type": "location_select", 
                "key": "rselfmenu_2_0"
            },
            {
               "type": "media_id", 
               "name": "图片", 
               "media_id": "MEDIA_ID1"
            }, 
            {
               "type": "view_limited", 
               "name": "图文消息", 
               "media_id": "MEDIA_ID2"
            }
        ]
    }
    View Code

    参数说明

    参数是否必须说明
    button 一级菜单数组,个数应为1~3个
    sub_button 二级菜单数组,个数应为1~5个
    type 菜单的响应动作类型
    name 菜单标题,不超过16个字节,子菜单不超过40个字节
    key click等点击类型必须 菜单KEY值,用于消息接口推送,不超过128字节
    url view类型必须 网页链接,用户点击菜单可打开链接,不超过256字节
    media_id media_id类型和view_limited类型必须 调用新增永久素材接口返回的合法media_id


    返回结果

    正确时的返回JSON数据包如下:{"errcode":0,"errmsg":"ok"}

    错误时的返回JSON数据包如下(示例为无效菜单名长度):{"errcode":40018,"errmsg":"invalid button name size"}

    根据以上的信息,我们进行封装一下菜单。

    我们现在能用的是一个点击click(点击事件)和view(访问网页)这俩种类型。

    菜单的结构:

    click和view的请求示例

    {
         "button":[
         {    
              "type":"click",
              "name":"今日歌曲",
              "key":"V1001_TODAY_MUSIC"
          },
          {
               "name":"菜单",
               "sub_button":[
               {    
                   "type":"view",
                   "name":"搜索",
                   "url":"http://www.soso.com/"
                },
                {
                   "type":"view",
                   "name":"视频",
                   "url":"http://v.qq.com/"
                },
                {
                   "type":"click",
                   "name":"赞一下我们",
                   "key":"V1001_GOOD"
                }]
           }]
     }
    View Code

    接下来是对菜单结构的封装。因为我们是采用面向对象的编程方式,最终提交的json格式菜单数据就应该是由对象直接转换得到,而不是在程序代码中拼一大堆json数据。菜单结构封装的依据是公众平台API文档中给出的那一段json格式的菜单结构,如下所示:

    导入jar包

    首先是 菜单项的基类,所有一级菜单、二级菜单都共有一个相同的属性,那就是name。菜单项基类的封装代码如下:

    package com.souvc.weixin.menu;
    
    /**
    * 类名: Button </br>
    * 包名: com.souvc.weixin.menu
    * 描述: 菜单项的基类  </br>
    * 开发人员: souvc  </br>
    * 创建时间:  2015-12-1 </br>
    * 发布版本:V1.0  </br>
     */
    public class Button {
        
        private String name;//所有一级菜单、二级菜单都共有一个相同的属性,那就是name
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    View Code

    接着是子菜单项的封装。这里对子菜单是这样定义的:没有子菜单的菜单项,有可能是二级菜单项,也有可能是不含二级菜单的一级菜单。这类子菜单项一定会包含三个属性:type、name和key,封装的代码如下:

    package com.souvc.weixin.menu;
    
    /**
    * 类名: CommonButton </br>
    * 包名: com.souvc.weixin.menu
    * 描述: 子菜单项 :没有子菜单的菜单项,有可能是二级菜单项,也有可能是不含二级菜单的一级菜单。 </br>
    * 开发人员: souvc  </br>
    * 创建时间:  2015-12-1 </br>
    * 发布版本:V1.0  </br>
     */
    public class CommonButton extends Button {
        
        private String type;
        private String key;
    
        public String getType() {
            return type;
        }
    
        public void setType(String type) {
            this.type = type;
        }
    
        public String getKey() {
            return key;
        }
    
        public void setKey(String key) {
            this.key = key;
        }
    }
    View Code

     再往下是父菜单项的封装。对父菜单项的定义:包含有二级菜单项的一级菜单。这类菜单项包含有二个属性:name和sub_button,而sub_button以是一个子菜单项数组。父菜单项的封装代码如下:

    package com.souvc.weixin.menu;
    
    /**
    * 类名: ComplexButton </br>
    * 包名: com.souvc.weixin.menu
    * 描述: 父菜单项 :包含有二级菜单项的一级菜单。这类菜单项包含有二个属性:name和sub_button,而sub_button以是一个子菜单项数组 </br>
    * 开发人员: souvc  </br>
    * 创建时间:  2015-12-1 </br>
    * 发布版本:V1.0  </br>
     */
    public class ComplexButton extends Button {
        private Button[] sub_button;
    
        public Button[] getSub_button() {
            return sub_button;
        }
    
        public void setSub_button(Button[] sub_button) {
            this.sub_button = sub_button;
        }
    }
    View Code

    最后是整个菜单对象的封装,菜单对象包含多个菜单项(最多只能有3个),这些菜单项即可以是子菜单项(不含二级菜单的一级菜单),也可以是父菜单项(包含二级菜单的菜单项),如果能明白上面所讲的,再来看封装后的代码就很容易理解了:

    package com.souvc.weixin.menu;
    
    /**
    * 类名: Menu </br>
    * 包名: com.souvc.weixin.menu
    * 描述: 整个菜单对象的封装 </br>
    * 开发人员:souvc </br>
    * 创建时间:  2015-12-1 </br>
    * 发布版本:V1.0  </br>
     */
    public class Menu {
        private Button[] button;
    
        public Button[] getButton() {
            return button;
        }
    
        public void setButton(Button[] button) {
            this.button = button;
        }
    }
    View Code

    关于菜单的POJO类的封装就介绍完了。

    AccessToken 的POJO的封装:

    package com.souvc.weixin.pojo;
    
    /**
    * 类名: AccessToken </br>
    * 包名: com.souvc.weixin.pojo
    * 描述: 微信通用接口凭证  </br>
    * 开发人员:souvc </br>
    * 创建时间:  2015-12-1 </br>
    * 发布版本:V1.0  </br>
     */
    public class AccessToken {
        // 获取到的凭证
        private String token;
        // 凭证有效时间,单位:秒
        private int expiresIn;
    
        public String getToken() {
            return token;
        }
    
        public void setToken(String token) {
            this.token = token;
        }
    
        public int getExpiresIn() {
            return expiresIn;
        }
    
        public void setExpiresIn(int expiresIn) {
            this.expiresIn = expiresIn;
        }
    }
    View Code

    封装通用的请求方法

    读到这里,就默认大家已经掌握了上面讲到的所有关于自定义菜单的理论知识,下面就进入代码实战讲解的部分。

    先前我们了解到,创建菜单需要调用二个接口,并且都是https请求,而非http。如果要封装一个通用的请求方法,该方法至少需要具备以下能力:

    1)支持HTTPS请求;

    2)支持GET、POST两种方式;

    3)支持参数提交,也支持无参数的情况;

    对于https请求,我们需要一个证书信任管理器,这个管理器类需要自己定义,但需要实现X509TrustManager接口,代码如下:

    package com.souvc.weixin.util;
    
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    
    import javax.net.ssl.X509TrustManager;
    
    /**
    * 类名: MyX509TrustManager </br>
    * 包名: com.souvc.weixin.util
    * 描述: 证书信任管理器(用于https请求)  </br>
    * 开发人员:souvc  </br>
    * 创建时间:  2015-12-1 </br>
    * 发布版本:V1.0  </br>
     */
    public class MyX509TrustManager implements X509TrustManager {
    
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }
    
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }
    
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    }
    View Code

    这个证书管理器的作用就是让它信任我们指定的证书,上面的代码意味着信任所有证书,不管是否权威机构颁发。

    证书有了,通用的https请求方法就不难实现了,实现代码如下:

    package com.souvc.weixin.util;
    
    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 net.sf.json.JSONException;
    import net.sf.json.JSONObject;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import com.souvc.weixin.menu.Menu;
    import com.souvc.weixin.pojo.AccessToken;
    
    /**
    * 类名: WeixinUtil </br>
    * 包名: com.souvc.weixin.util
    * 描述: 公众平台通用接口工具类 </br>
    * 开发人员: souvc  </br>
    * 创建时间:  2015-12-1 </br>
    * 发布版本:V1.0  </br>
     */
    public class WeixinUtil {
        
        private static Logger log = LoggerFactory.getLogger(WeixinUtil.class);
        
        // 获取access_token的接口地址(GET) 限200(次/天)
        public final static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
        
        // 菜单创建(POST) 限100(次/天)
        public static String menu_create_url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
    
        /**
         * 创建菜单
         * 
         * @param menu 菜单实例
         * @param accessToken 有效的access_token
         * @return 0表示成功,其他值表示失败
         */
        public static int createMenu(Menu menu, String accessToken) {
            int result = 0;
            // 拼装创建菜单的url
            String url = menu_create_url.replace("ACCESS_TOKEN", accessToken);
            // 将菜单对象转换成json字符串
            String jsonMenu = JSONObject.fromObject(menu).toString();
            // 调用接口创建菜单
            JSONObject jsonObject = httpRequest(url, "POST", jsonMenu);
            if (null != jsonObject) {
                if (0 != jsonObject.getInt("errcode")) {
                    result = jsonObject.getInt("errcode");
                    log.error("创建菜单失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));
                }
            }
    
            return result;
        }
        
        
        /**
         * 获取access_token
         * 
         * @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);
            JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
            // 如果请求成功
            if (null != jsonObject) {
                try {
                    accessToken = new AccessToken();
                    accessToken.setToken(jsonObject.getString("access_token"));
                    accessToken.setExpiresIn(jsonObject.getInt("expires_in"));
                } catch (JSONException e) {
                    accessToken = null;
                    // 获取token失败
                    log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));
                }
            }
            return accessToken;
        }
        
        
        /**
         * 描述:  发起https请求并获取结果
         * @param requestUrl 请求地址
         * @param requestMethod 请求方式(GET、POST)
         * @param outputStr 提交的数据
         * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
         */
        public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {
            JSONObject jsonObject = 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 httpUrlConn = (HttpsURLConnection) url.openConnection();
                httpUrlConn.setSSLSocketFactory(ssf);
    
                httpUrlConn.setDoOutput(true);
                httpUrlConn.setDoInput(true);
                httpUrlConn.setUseCaches(false);
                
                // 设置请求方式(GET/POST)
                httpUrlConn.setRequestMethod(requestMethod);
    
                if ("GET".equalsIgnoreCase(requestMethod))
                    httpUrlConn.connect();
    
                // 当有数据需要提交时
                if (null != outputStr) {
                    OutputStream outputStream = httpUrlConn.getOutputStream();
                    // 注意编码格式,防止中文乱码
                    outputStream.write(outputStr.getBytes("UTF-8"));
                    outputStream.close();
                }
    
                // 将返回的输入流转换成字符串
                InputStream inputStream = httpUrlConn.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;
                httpUrlConn.disconnect();
                jsonObject = JSONObject.fromObject(buffer.toString());
            } catch (ConnectException ce) {
                log.error("Weixin server connection timed out.");
            } catch (Exception e) {
                log.error("https request error:{}", e);
            }
            return jsonObject;
        }
    }
    View Code

    添加菜单管理器:

    package com.souvc.weixin.main;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import com.souvc.weixin.menu.Button;
    import com.souvc.weixin.menu.CommonButton;
    import com.souvc.weixin.menu.ComplexButton;
    import com.souvc.weixin.menu.Menu;
    import com.souvc.weixin.pojo.AccessToken;
    import com.souvc.weixin.util.WeixinUtil;
    
    /**
    * 类名: MenuManager </br>
    * 包名: com.souvc.weixin.main
    * 描述:菜单管理器类 </br>
    * 开发人员: liuhf </br>
    * 创建时间:  2015-12-1 </br>
    * 发布版本:V1.0  </br>
     */
    public class MenuManager {
        private static Logger log = LoggerFactory.getLogger(MenuManager.class);
    
        public static void main(String[] args) {
            // 第三方用户唯一凭证
            String appId = "000000000000000000";
            // 第三方用户唯一凭证密钥
            String appSecret = "00000000000000000000000000000000";
    
            // 调用接口获取access_token
            AccessToken at = WeixinUtil.getAccessToken(appId, appSecret);
    
            if (null != at) {
                // 调用接口创建菜单
                int result = WeixinUtil.createMenu(getMenu(), at.getToken());
    
                // 判断菜单创建结果
                if (0 == result)
                    log.info("菜单创建成功!");
                else
                    log.info("菜单创建失败,错误码:" + result);
            }
        }
    
        /**
         * 组装菜单数据
         * 
         * @return
         */
        private static Menu getMenu() {
            CommonButton btn11 = new CommonButton();
            btn11.setName("天气预报");
            btn11.setType("click");
            btn11.setKey("11");
    
            CommonButton btn12 = new CommonButton();
            btn12.setName("公交查询");
            btn12.setType("click");
            btn12.setKey("12");
    
            CommonButton btn13 = new CommonButton();
            btn13.setName("周边搜索");
            btn13.setType("click");
            btn13.setKey("13");
    
            CommonButton btn14 = new CommonButton();
            btn14.setName("历史上的今天");
            btn14.setType("click");
            btn14.setKey("14");
    
            CommonButton btn21 = new CommonButton();
            btn21.setName("歌曲点播");
            btn21.setType("click");
            btn21.setKey("21");
    
            CommonButton btn22 = new CommonButton();
            btn22.setName("经典游戏");
            btn22.setType("click");
            btn22.setKey("22");
    
            CommonButton btn23 = new CommonButton();
            btn23.setName("美女电台");
            btn23.setType("click");
            btn23.setKey("23");
    
            CommonButton btn24 = new CommonButton();
            btn24.setName("人脸识别");
            btn24.setType("click");
            btn24.setKey("24");
    
            CommonButton btn25 = new CommonButton();
            btn25.setName("聊天唠嗑");
            btn25.setType("click");
            btn25.setKey("25");
    
            CommonButton btn31 = new CommonButton();
            btn31.setName("Q友圈");
            btn31.setType("click");
            btn31.setKey("31");
    
            CommonButton btn32 = new CommonButton();
            btn32.setName("电影排行榜");
            btn32.setType("click");
            btn32.setKey("32");
    
            CommonButton btn33 = new CommonButton();
            btn33.setName("幽默笑话");
            btn33.setType("click");
            btn33.setKey("33");
    
            
            /**
             * 微信:  mainBtn1,mainBtn2,mainBtn3底部的三个一级菜单。
             */
            
            ComplexButton mainBtn1 = new ComplexButton();
            mainBtn1.setName("生活助手");
            //一级下有4个子菜单
            mainBtn1.setSub_button(new CommonButton[] { btn11, btn12, btn13, btn14 });
    
            
            ComplexButton mainBtn2 = new ComplexButton();
            mainBtn2.setName("休闲驿站");
            mainBtn2.setSub_button(new CommonButton[] { btn21, btn22, btn23, btn24, btn25 });
    
            
            ComplexButton mainBtn3 = new ComplexButton();
            mainBtn3.setName("更多体验");
            mainBtn3.setSub_button(new CommonButton[] { btn31, btn32, btn33 });
    
            
            /**
             * 封装整个菜单
             */
            Menu menu = new Menu();
            menu.setButton(new Button[] { mainBtn1, mainBtn2, mainBtn3 });
    
            return menu;
        }
    }
    View Code

    注意替换称自己的appId和appSecret。

    添加log4j.properties

        log4j.rootLogger=info,console,file  
          
        log4j.appender.console=org.apache.log4j.ConsoleAppender  
        log4j.appender.console.layout=org.apache.log4j.PatternLayout  
        log4j.appender.console.layout.ConversionPattern=[%-5p] %m%n  
          
        log4j.appender.file=org.apache.log4j.DailyRollingFileAppender  
        log4j.appender.file.DatePattern='-'yyyy-MM-dd  
        log4j.appender.file.File=./logs/weixinmpmenu.log  
        log4j.appender.file.Append=true  
        log4j.appender.file.layout=org.apache.log4j.PatternLayout  
        log4j.appender.file.layout.ConversionPattern=[%-5p] %d %37c %3x - %m%n  

    直接执行MenuManager 的main 方法即可。

    效果如下:

    注意由于我们没有写处理事件,点击这些按钮是没有反应的。

    参照了柳峰老师的一些教程,然后自己写了一下,现在分享给大家。有什么不对的,请大家多多指教。

    其他文章关联:

    第一篇:微信公众平台开发实战Java版之了解微信公众平台基础知识以及资料准备

    第二篇 :微信公众平台开发实战Java版之开启开发者模式,接入微信公众平台开发

    第三篇 :微信公众平台开发实战Java版之请求消息,响应消息以及事件消息类的封装

    第四篇 :微信公众平台开发实战Java版之完成消息接受与相应以及消息的处理

    第五篇 :微信公众平台开发实战Java版之如何获取公众号的access_token以及缓存access_token

    第六篇 :微信公众平台开发实战Java版之如何自定义微信公众号菜单

    第七篇 :微信公众平台开发实战Java版之如何获取微信用户基本信息

    第八篇 :微信公众平台开发实战Java版之如何网页授权获取用户基本信息

    第九篇 :微信公众平台开发实战Java版之如何实现自定义分享内容

    其他:Web开发须知:URL编码与解码

  • 相关阅读:
    hdu 4710 Balls Rearrangement()
    hdu 4707 Pet(DFS水过)
    hdu 4706 Children's Day(模拟)
    hdu 4712 Hamming Distance(随机函数暴力)
    csu 1305 Substring (后缀数组)
    csu 1306 Manor(优先队列)
    csu 1312 榜单(模拟题)
    csu 1303 Decimal (数论题)
    网络爬虫
    Python处理微信利器——itchat
  • 原文地址:https://www.cnblogs.com/liuhongfeng/p/4857312.html
Copyright © 2011-2022 走看看