微信开发 获取当前自定义菜单_微信公众号怎么获取用户点击自定义菜单获取菜单id - CSDN
精华内容
参与话题
  • 在上篇文章实现微信扫码获取带指定参数时,微信公众号后台启用了服务器配置,导致之前设置好的微信自定义菜单失效。 开启了服务器配置,就无法用之前的方式在微信公众平台配置自定义菜单 此时,我们要通过接口来...

    在上篇文章实现微信扫码获取带指定参数时,微信公众号后台启用了服务器配置,导致之前设置好的微信自定义菜单失效。
    这里写图片描述
    开启了服务器配置,就无法用之前的方式在微信公众平台配置自定义菜单
    此时,我们要通过接口来配置微信自定义菜单,微信自定义菜单接口链接

    这里写图片描述

    首先,我们打开微信公众平台接口调试工具 接口调试工具
    1.通过appid以及secret来获取access_token,
    这里写图片描述

    2.组织创建菜单内容。微信提供了许多不同功能的按钮类型,可根据实际需求组织创建。
    这里简单的创建两个1级view类型按钮:即用户点击按钮实现跳转URL。

    {
        "button": [
            {
                "type": "view", 
                "name": "百度", 
                "url": "http://www.baidu.com"
            }, 
            {
                "type": "view", 
                "name": "淘宝", 
                "url": "http://www.taobao.com"
            }
        ]
    }

    3.在接口调试工具- - -选择 ‘自定义菜单’的接口类型,填入access_token,以及刚刚构建好的菜单内容。

    这里写图片描述

    点击检查问题,如果填写参数正确,则请求成功。
    这里写图片描述

    此时,重新打开或重新关注公众号,即能看到刚刚构建的自定义菜单已经生效。

    展开全文
  • 请先读完本文再进行配置开发 ...如何在微信自定义菜单中将用户导航到个人中心页面呢? 首选需要通过用户点击获取用户openid,而通过用户的点击跳转获取用户openid就必须在菜单中动态绑定用户的openid,或者

    请先读完本文再进行配置开发
    请先前往微信平台开发者文档阅读“网页授权获取用户基本信息”的接口说明

    在微信公众账号开发中,往往有定义一个菜单,然后用户点击该菜单就进入用户个人中心的功能,通常应用于各个公众账号中的会员服务。

    如何在微信自定义菜单中将用户导航到个人中心页面呢?
    首选需要通过用户点击获取用户openid,而通过用户的点击跳转获取用户openid就必须在菜单中动态绑定用户的openid,或者在菜单的跳转URL中填写微信提供的链接,官方给了两个链接类型

    一种是Scope为snsapi_base的链接

    https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx520c15f417810387&redirect_uri=https%3A%2F%2Fchong.qq.com%2Fphp%2Findex.php%3Fd%3D%26c%3DwxAdapter%26m%3DmobileDeal%26showwxpaytitle%3D1%26vb2ctag%3D4_2030_5_1194_60&response_type=code&scope=snsapi_base&state=123#wechat_redirect
    

    另一种是Scope为snsapi_userinfo的链接

    https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect
    

    这两种链接的区别如下

    应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息)
    

    网上很多说法是将链接的url直接作为微信自定义菜单中view类型中的url(在填写是url时需要配置网页授权回调域名和appid),本人试了一下这种做法然而不能成功

     {
        "type":"view",
        "name":"会员中心",
        "url":"https://open.weixin.qq.com/connect/oauth2/authorize?appid=你的appid&redirect_uri=你配置接收微信认证的地址?response_type=code&scope=snsapi_base&state=1#wechat_redirect"
     },
    

    返回结果是创建菜单失败

    创建菜单失败  errcode:{40033} errmsg:{invalid charset. please check your request, if include \uxxxx will create fail! hint: [91..gA0792vr23]}
    

    我试了一下将后面的地址进行urlEncode,还是同样的错误。

    后来我想了一个办法

    在自定义菜单中填写自己的url,在填写的url中将用户重定向到snsapi_base的url中,然后再在snsapi_base中配置获取用户openid以及用户其他信息,最后跳转到一个页面,也就是通常的会员中心页面。

    流程如下

    处理流程

    请看代码

    {
        "type":"view",
        "name":"会员中心",      
        "url":"http://配置的网址/redirect"
    }
    

    其中通过url将用户跳转到

    http://配置的网址/redirect

    然后在处理方法中调用一次重定向即可

    
    //类上的配置
    @Controller
    @RequestMapping("/wechat")
    public class WeChatController{
        @RequestMapping(value = "/redirect", method = RequestMethod.GET)
        public String weixinRedirect(HttpServletRequest request, HttpServletResponse response) {
            return "redirect:https://open.weixin.qq.com/connect/oauth2/authorize?appid=你的appid&redirect_uri=你的服务器处理地址?response_type=code&scope=snsapi_base&state=1&connect_redirect=1#wechat_redirect";
            }
    }

    服务器会将微信认证 跳转到你的服务器处理地址,也就是上面

    redirect_uri=你的服务器处理地址中的地址
    

    这里配置为

    你的服务器地址/oauth
    

    代码如下

    @RequestMapping(value = "/oauth", method = RequestMethod.GET)
        public String weixinOAuth(HttpServletRequest request, HttpServletResponse response, Model model) {
            //得到code
            String CODE = request.getParameter("code");
            String APPID = "你的APPID";
            String SECRET = "你的SECRET";
            //换取access_token 其中包含了openid
            String URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code".replace("APPID", APPID).replace("SECRET", SECRET).replace("CODE", CODE);
            //URLConnectionHelper是一个模拟发送http请求的类
            String jsonStr = URLConnectionHelper.sendGet(URL);
            //System.out.println(jsonStr);
            //out.print(jsonStr);
            JSONObject jsonObj = new JSONObject(jsonStr);
            String openid = jsonObj.get("openid").toString();
            //有了用户的opendi就可以的到用户的信息了
            //地址为https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
            //得到用户信息之后返回到一个页面
            model.addAttribute("user", wechatUser);
            return "vip/userInfo";
        }

    效果如下

    这里写图片描述

    这里写图片描述

    而且这种方式当用户用其他浏览器打开时,会出错,保证了只能在微信中使用,保障了安全性。而且地址栏不会有其他用户个人信息的暴露。

    这里写图片描述

    参考文献

    网页授权获取用户基本信息

    展开全文
  • 微信开发 -- 自定义菜单

    万次阅读 2014-02-10 10:18:49
    微信开发 自定义菜单 菜单事件推送

    目前微信服务号自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单。一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“...”代替。请注意,创建自定义菜单后,由于微信客户端缓存,需要24小时微信客户端才会展现出来。建议测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果。

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

    click:
    用户点击click类型按钮后,微信服务器会通过消息接口推送消息类型为event	的结构给开发者(参考消息接口指南),并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互;
    view:
    用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的url值	(即网页链接),达到打开网页的目的,建议与网页授权获取用户基本信息接口结合,获得用户的登入个人信息。
    


    接口调用请求说明

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

    请求示例(JSON数据请使用UTF-8编码)

    {
        "button":[
            {"type":"click","name":"我的信息","sub_button":[
                {"type":"click","name":"拇指查询","key":"BUTTON_1"},
    	    {"type":"click","name":"拇指请假","key":"BUTTON_2"},
    	    {"type":"view","name":"工号绑定","url":"http://XXXXXXXXXXXXXXXXX"}]
    	},
            {"type":"click","name":"业务流程","key":"BUTTON_3"},
            {"name":"员工建议","sub_button":[
                {"type":"view","name":"思想火花","url":"http://XXXXXXXXXXXXXXXXXX"},
                {"type":"view","name":"奖品兑换","url":"http://XXXXXXXXXXXXXXXXXX"},
                {"type":"click","name":"赞一下我们","key":"BUTTON_ZAN"}]
            }
        ]
    }
    

    参数说明

    参数 是否必须 说明
    button 一级菜单数组,个数应为1~3个
    sub_button 二级菜单数组,个数应为1~5个
    type 菜单的响应动作类型,目前有click、view两种类型
    name 菜单标题,不超过16个字节,子菜单不超过40个字节
    key click类型必须 菜单KEY值,用于消息接口推送,不超过128字节
    url view类型必须 网页链接,用户点击菜单可打开链接,不超过256字节


    返回结果

    正确时的返回JSON数据包如下:

    {"errcode":0,"errmsg":"ok"}
    

    错误时的返回JSON数据包如下(示例为无效菜单名长度):

    {"errcode":40018,"errmsg":"invalid button name size"}

            以下是示例代码(PHP)。

            

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN">
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
            <meta name="author" content="Chris Mao" />
        </head>
        <body>
        <?php
            $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
    
            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_HEADER, 0);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_POST, 0);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
    
            $output = curl_exec($ch);
            curl_close($ch);
            if (empty($output)) { var_dump($output); exit; }
            $result = json_decode($output);
            $token = $result->access_token;
                
            //创建菜单
            $url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=$token";
            $jsonData = file_get_contents("menu.json");
            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_HEADER, 0);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
            curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
            $output = curl_exec($ch);
            curl_close($ch); 
            var_dump($output);
        ?>
        </body>
    </html>

    menu.json

    {
        "button":[
            {"type":"click","name":"我的信息","sub_button":[
                {"type":"click","name":"拇指查询","key":"BUTTON_1"},
    	    {"type":"click","name":"拇指请假","key":"BUTTON_2"},
    	    {"type":"view","name":"工号绑定","url":"http://XXXXXXXXXXXXXXXXX"}]
    	},
            {"type":"click","name":"业务流程","key":"BUTTON_3"},
            {"name":"员工建议","sub_button":[
                {"type":"view","name":"思想火花","url":"http://XXXXXXXXXXXXXXXXXX"},
                {"type":"view","name":"奖品兑换","url":"http://XXXXXXXXXXXXXXXXXX"},
                {"type":"click","name":"赞一下我们","key":"BUTTON_ZAN"}]
            }
        ]
    }

    响应自定义菜单事件

        $wechatObj = new wechatCallbackAPI();
        if (isset($_GET["echostr"])) { 
            $wechatObj->valid(); 
        } else { 
            $wechatObj->responseMsg();
        }
    
    
        class wechatCallbackAPI {
    
            private $token = "WEIXIN";
    
            private $appId = "APPID";
    
            private $appSecret = "APPSECRET";
            
            private function checkSignature() {
                $signature = $_GET["signature"];
                $timestamp = $_GET["timestamp"];
                $nonce = $_GET["nonce"];    
                        
                $tmpArr = array($this->token, $timestamp, $nonce);
                sort($tmpArr);
                $tmpStr = implode($tmpArr);
                $tmpStr = sha1($tmpStr);
                
                if($tmpStr == $signature) {
                    return true;
                } else {
                    return false;
                }
            }
    
            private function getAccessToken() {
                $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$this->appId&secret=$this->appSecret";
    
                $ch = curl_init($url);
                $curl_setopt($ch, CURLOPT_HEADER, 0);
                $curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
                $curl_setopt($ch, CURLOPT_POST, 0);
                $curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
                $curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
    
                $output = curl_exec($ch);
                curl_close($ch);
                if (empty($output)) { return ""; }
    
                $result = json_decode($result);
                return $result->access_token;
            }
    
            public function valid() {
                $echoStr = $_GET["echostr"];
                
                //valid signature, option
                if($this->checkSignature()){
                    echo $echoStr;
                    exit;
                }
            }
        
            public function responseMsg() {
                //get post data, May be due to the different environments
                $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
                if (empty($postStr)){
                    echo "";
                    exit;
                }
    
                //extract post data
                $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
                $fromUsername = $postObj->FromUserName;
                $toUsername = $postObj->ToUserName;
                $time = time();
    
                //文本消息模板
                $textTpl = "<xml>
                            <ToUserName><![CDATA[%s]]></ToUserName>
                            <FromUserName><![CDATA[%s]]></FromUserName>
                            <CreateTime>%s</CreateTime>
                            <MsgType><![CDATA[%s]]></MsgType>
                            <Content><![CDATA[%s]]></Content>
                            <FuncFlag>0</FuncFlag>
                            </xml>";
                
                switch (strtolower(trim($postObj->MsgType))) {
                    case "text": //文本消息
                        $keyword = trim($postObj->Content);
                        if(!empty($keyword)) {
                            $msgType = "text";
                            $contentStr = "$fromUsername, 您发送了文本信息: $keyword ";
                            if (strtolower($keyword) == "time") {
                                $contentStr = date("Y-m-d H:i:s", $time);
                            }
                            $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
                        } else {
                            $resultStr = "Input something...";
                        }
                        break;
                    case "image": //图片消息
                        $msgType = "text";
                        $contentStr = "$fromUsername, 您发送了图片信息";
                        $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
                        break;
                    case "voice": //声音消息
                        $msgType = "text";
                        $contentStr = "$fromUsername, 您发送了声音信息";
                        $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
                        break;
                    case "video": //视频消息
                        $msgType = "text";
                        $contentStr = "$fromUsername, 您发送了视频信息";
                        $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
                        break;
                    case "location": //位置消息
                        $msgType = "text";
                        $contentStr = "$fromUsername, 您发送了位置信息";
                        $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
                        break;
                    case "link": //链接消息
                        $msgType = "text";
                        $contentStr = "$fromUsername, 您发送了链接信息";
                        $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
                        break;
                    case "event": //事件
                        switch (strtolower(trim($postObj->Event))) {
                            case "subscribe": //关注事件
                                $msgType = "text";
                                $contentStr = "欢迎您关注XXXXXXX";
                                $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
                                break;
                            case "unsubscribe": //取消关注事件
                                break;
                            case "scan": //用户已关注时扫描二维码事件
                                $msgType = "text";
                                $contentStr = "$fromUsername, 您扫描了二维码";
                                $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
                                break;
                            case "location": //上传地理位置事件
                                $msgType = "text";
                                $contentStr = "$fromUsername, 您上传地理位置";
                                $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
                                break;
                            case "click": //自定义菜单事件
                                $msgType = "text";
                                $contentStr = "$fromUsername, 您点击了自定义菜单 $postObj->EventKey ";
                                if ("BUTTON_ZAN" == $postObj->EventKey) {
                                    $contentStr = "感谢您的赞,我们会继续提供更优质的服务。";
                                }
                                $resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
                                ;
                                break;
                            default:
                                $resultStr = "";
                        }
                        break;
                    default:
                        $resultStr = "";
                }
                echo $resultStr;
            }
        }
    ?>

    自定义菜单查询

    使用接口创建自定义菜单后,开发者还可使用接口查询自定义菜单的结构。

    请求说明

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

    返回说明

    对应创建接口,正确的Json返回结果:
     {"menu":{"button":[{"name":"我的信息","sub_button":[{"type":"click","name":"拇指查询","key":"BUTTON_1","sub_button":[]},{"type":"click","name":"拇指请假","key":"BUTTON_2","sub_button":[]},{"type":"view","name":"工号绑定","url":"http:\/\/XXXXXXXX","sub_button":[]}]},{"type":"click","name":"业务流程","key":"BUTTON_3","sub_button":[]},{"name":"员工建议","sub_button":[{"type":"view","name":"思想火花","url":"http:\/\/XXXXXXXX","sub_button":[]},{"type":"view","name":"奖品兑换","url":"http:\/\/XXXXXXXX","sub_button":[]},{"type":"click","name":"赞一下我们","key":"BUTTON_ZAN","sub_button":[]}]}]}}


    自定义菜单删除

    使用接口创建自定义菜单后,开发者还可使用接口删除当前使用的自定义菜单。

    请求说明

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

    返回说明

    对应创建接口,正确的Json返回结果:
    {"errcode":0,"errmsg":"ok"}


    展开全文
  • 自定义菜单获取用户信息详细介绍以及苹果设备的空指针异常处理

    之前写过一篇微信公众号开发–自定义菜单跳转页面并获取用户信息

    由于当时是刚学习微信公众号开发当时的思路虽然可行,不过不是最好的,最近也用到了需要获取用户信息的地方,再次整理一下。

    流程

    这里写图片描述

    注意点

    ###第一
    如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。

    必须是在微信客户端

    第二

    通过code换取网页授权access_token
    
    code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。
    

    在第二步中通过code换取网页授权access_token,这个access_token和其他接口中使用的access_token,不一样。

    如果是静默授权,这里的access_token没什么用,反而是换取access_token时得到的openid非常有用

    code只能用一次,注意这里的access_token和基础接口中的access_token不一样

    实现步骤

    第一步: 配置网页授权回调域名

    开发 - 接口权限 - 网页服务 - 网页授权 - 网页授权获取用户基本信息

    配置网页授权域名

    我这个是测试号,花生壳做的域名

    这里写图片描述

    域名不需要配置http://开头,也不需要项目路径

    第二步: 生成网页链接

    这里有两种一种是scope为snsapi_base另一种是scope为snsapi_userinfo

    snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid)

    snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息)

    先看第一种

    官方给的示例

    https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx520c15f417810387&redirect_uri=https%3A%2F%2Fchong.qq.com%2Fphp%2Findex.php%3Fd%3D%26c%3DwxAdap
    ter%26m%3DmobileDeal%26showwxpaytitle%3D1%26vb2ctag%3D4_2030_5_1194_60&response_type=code&scope=snsapi_bas
    e&state=123#wechat_redirect
    

    前缀为

    https://open.weixin.qq.com/connect/oauth2/authorize?appid=你的appId&redirect_uri=
    

    后缀为

    &response_type=code&scope=snsapi_base&state=123#wechat_redirect
    

    顺序必能乱,中间是你的网址。网址还经过了转码处理,java使用java.net.URLEncoder提供的encode方法即可

    好了,这里我们拼接一个我们的链接

    http://gwchsk.imwork.net/myair/auth/user
    

    我们在这个地址下获取用户信息,encode之后为

    http%3A%2F%2Fgwchsk.imwork.net%2Fmyair%2Fauth%2Fuser

    然后将前缀和后缀都拼接好,不要忘了前缀中的appId

    拼接好之后在浏览器输入,找个能生成二维码的浏览器,再扫描一下

    一个简单的Controller如下,我们只是看第一步是否可以得到code

    package com.airport.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    
    @Controller
    @RequestMapping("/auth")
    public class WechatUserController {
    	@RequestMapping(path = "/user", method = RequestMethod.GET)
    	public String user(String code) {
    		System.out.println("得到的code:"+code);
    		return "user";
    	}
    }
    
    

    用微信扫码之后就可以看到控制台输出了code

    第三步:通过code换取网页授权access_token,这里会得到openId

    通过code就可以换取access_token了,这里其实openId比access_token更有用

    换取的结果

    {"access_token":"0W71gcY7yvLBBL0JzxQC-sGJnBBOHcx95sBZqpWOTJpNm5C1Tm00UnE1MsNXaFb0JQmDwWHFtGi7vkuHT0ohdf5a8w2FI-JXKYtOKe5ehSs","expires_in":7200,"refresh_token":"QhEl0tbQERkdD-7-JKFZfYh68wqx-Frrs1OnHv5Wv7CarYhDhtk2nATc_x2q5liPMs0Pdlh-FOgJgcIDy0d57wqa431BJ2NVjZKWyWA-hpQ","openid":"oJhxHv8xKWX6ZYo4aeqBhTko94jk","scope":"snsapi_base"}
    

    我写了一个方法

    ExchangeCode2OpenId.java

    package com.airport.util;
    
    import org.json.JSONException;
    import org.json.JSONObject;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import com.airport.context.WechatConst;
    
    /**
     * 通过code换取openId
     * 
     * @author 程高伟
     *
     * @date 2016年10月17日 下午7:05:34
     */
    public class ExchangeCode2OpenId {
    	private static Logger logger = LoggerFactory.getLogger(ExchangeCode2OpenId.class);
    
    	public static String exchange(String code) {
    		String openid = "";
    		String appId = WechatConst.appId;
    		String appSecret = WechatConst.appSecret;
    		// 换取access_token 其中包含了openid
    		// 这里通过code换取的是一个特殊的网页授权access_token,与基础支持中的access_token(该access_token用于调用其他接口)不同。
    		String URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code"
    				.replace("APPID", appId).replace("SECRET", appSecret).replace("CODE", code);
    		String jsonStr = HttpUtil.sendGet(URL);
    		logger.info("----------换取openid返回的结果:{}----------", jsonStr);
    		JSONObject jsonObj = new JSONObject(jsonStr);
    		try {
    			openid = jsonObj.getString("openid");
    		} catch (JSONException e) {
    			logger.info("----------换取openid发生了异常:{}----------", e.getMessage());
    		}
    		return openid;
    	}
    }
    
    

    好了,有了openid,我们就可以调用基础接口(用户管理)中的获取用户信息方法了。

    这里获取用户信息需要access_token,注意,这里的access_token不是上面通过code换取到的,而是要自己去获取。

    获取地址

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

    这样就可以获取用户信息了。

    整个流程就是这个样子。

    自定义菜单获取用户信息

    将上一步我们拼接的url作为view型按钮的url

    这里如果生成自定义菜单

    如果报40033错误,请参考

    微信公众号开发–自定义菜单失败40033解决方案

    自定义菜单创建好之后,就可以当用户点击自定义菜单的时候获取用户信息了。

    具体的获取用户信息接口地址

    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public String user(String code, Model model, HttpSession session) {
    	String openid = ExchangeCode2OpenId.exchange(code);
    	WechatUser wechatUser = WechatUserUtil.getWechatUser(openid);
    	if (null == wechatUser) {
    		model.addAttribute("title", "请从微信公众号进入");
    		return "notInWechat";
    	}
    	System.out.println("wechatUser:" + wechatUser);
    	session.setAttribute("wechatUser", wechatUser);
    	model.addAttribute("wechatUser", wechatUser);
    	model.addAttribute("title", "用户信息");
    	return "user";
    }
    

    我们看一下效果

    自定义菜单

    这里写图片描述

    点击后进入

    这里写图片描述

    点击最下面的按钮

    这里写图片描述

    确定后跳转到另一个页面

    这里写图片描述

    然后返回,仍然回到了用户信息页面

    这里写图片描述

    一切都很顺利

    看一下打印输出

    这里写图片描述

    如果你也很正常,那恭喜你,你用的安卓设备

    我们换成苹果的

    再走一遍,先清空打印内容,再看一下iPhone操作的打印输出内容

    这里写图片描述

    iPhone会在点击返回的时候再请求一次,而上面我们说过code只能用一次,第二次已经不能用了,所以获取不到用户信息,结果导致出错

    如果你用iPad,效果和安卓手机一样。

    搞不清楚为什么苹果iPhone和iPad居然不一样。

    好了,言归正传,如何解决这个问题,其实很简单,在第一次获取到用户信息后保存起来,具体存哪里根据你的喜好(session也好,缓存也好)

    这里我将它放在session里。

    献上关键代码

    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public String user(String code, Model model, HttpSession session) {
    	WechatUser wechatUser = getWechatUser(code, session);
    	if (null == wechatUser) {
    		model.addAttribute("title", "请从微信公众号进入");
    		return "notInWechat";
    	}
    	System.out.println("wechatUser:" + wechatUser);
    	session.setAttribute("wechatUser", wechatUser);
    	model.addAttribute("wechatUser", wechatUser);
    	model.addAttribute("title", "用户信息");
    	return "user";
    }
    
    private WechatUser getWechatUser(String code, HttpSession session) {
    	String openid = "";
    	WechatUser wechatUser = null;
    	WechatUser wechatUserInSession = (WechatUser) session.getAttribute("wechatUser");
    	// 先看session
    	if (null != wechatUserInSession) {
    		wechatUser = wechatUserInSession;
    		System.out.println("session中有用户信息");
    	} else {// session中没有 去获取
    		System.out.println("得到的code:" + code);
    		if (StringUtils.isBlank(code)) {
    			return null;
    		}else{
    			openid = ExchangeCode2OpenId.exchange(code);
    			if (StringUtils.isBlank(openid)) {
    				return null;
    			}
    		}
    		System.out.println("得到的openid:" + openid);
    		// 这里使用的是普通接口(用户管理,获取用户基本信息)
    		wechatUser = WechatUserUtil.getWechatUser(openid);
    		String headImgurl = wechatUser.getHeadimgurl();
    		// 132*132的头像
    		if (StringUtils.isNotBlank(wechatUser.getHeadimgurl())) {
    			headImgurl = headImgurl.substring(0, headImgurl.length() - 1);
    			headImgurl += "132";
    			wechatUser.setHeadimgurl(headImgurl);
    		} else {
    			headImgurl = "/assets/img/default_head.png";
    		}
    		wechatUser.setHeadimgurl(headImgurl);
    		System.out.println("得到的用户信息:" + wechatUser);
    	}
    	return wechatUser;
    }
    

    还有一种就是需要用户点击同意后再获取用户信息,就不写了,用到了再说,思路都是一样的。

    WechatUserUtil.java

    
    
    import java.io.UnsupportedEncodingException;
    
    import org.apache.commons.lang3.StringUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import com.google.gson.Gson;
    import com.google.gson.JsonSyntaxException;
    
    @Component
    public class WechatUserUtil {
    	@Autowired
    	private AccessTokenUtil accessToken;
    	private final Logger logger = LoggerFactory.getLogger(this.getClass());
    
    	public WechatUser getWechatUser(String openid) {
    
    		String token = accessToken.getAccessToken();
    		String URL = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
    		String jsonResult = HttpUtil.sendGet(URL.replace("OPENID", openid).replace("ACCESS_TOKEN", token));
    		logger.info("----------获取到的用户信息,{}----------", jsonResult);
    		Gson gson = new Gson();
    		WechatUser wechatUser = null;
    		try {
    			wechatUser = gson.fromJson(new String(jsonResult.getBytes("ISO-8859-1"), "UTF-8"), WechatUser.class);
    			// 错误的openId
    			if (StringUtils.isBlank(wechatUser.getOpenid())) {
    				wechatUser = null;
    			}
    		} catch (JsonSyntaxException e) {
    			logger.info("----------解析json出错----------");
    			e.printStackTrace();
    		} catch (UnsupportedEncodingException e) {
    			e.printStackTrace();
    		}
    		return wechatUser;
    	}
    
    }
    
    

    WechatUser.java

    package com.jrbac.model.wechat;
    
    import java.io.Serializable;
    
    /**
     * 微信用户详细信息
     * 
     * @author GWCheng
     *
     */
    public class WechatUser implements Serializable {
    
    	/**
    	 * 用户的真实姓名,绑定后的名称
    	 */
    	private String name;
    
    	/**
    	 * 
    	 */
    	private static final long serialVersionUID = 8170898780424513965L;
    	/**
    	 * 用户是否订阅该公众号标识,值为0时,代表此用户没有关注该公众号,拉取不到其余信息。
    	 */
    	private int subscribe;
    	/**
    	 * 用户的标识,对当前公众号唯一
    	 */
    	private String openid;
    	/**
    	 * 用户的昵称
    	 */
    	private String nickname;
    	/**
    	 * 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
    	 */
    	private int sex;
    	/**
    	 * 用户的语言,简体中文为zh_CN
    	 */
    	private String language;
    	/**
    	 * 用户所在城市
    	 */
    	private String city;
    	/**
    	 * 用户所在省份
    	 */
    	private String province;
    	/**
    	 * 用户所在国家
    	 */
    	private String country;
    	/**
    	 * 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。
    	 * 若用户更换头像,原有头像URL将失效。
    	 */
    	private String headimgurl;
    	/**
    	 * 用户关注时间,为时间戳。如果用户曾多次关注,则取最后关注时间
    	 */
    	private long subscribe_time;
    	/**
    	 * 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。详见:获取用户个人信息(UnionID机制)
    	 */
    	private String unionid;
    	/**
    	 * 公众号运营者对粉丝的备注,公众号运营者可在微信公众平台用户管理界面对粉丝添加备注
    	 */
    	private String remark;
    	/**
    	 * 用户所在的分组ID
    	 */
    	private long groupid;
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public int getSubscribe() {
    		return subscribe;
    	}
    
    	public void setSubscribe(int subscribe) {
    		this.subscribe = subscribe;
    	}
    
    	public String getOpenid() {
    		return openid;
    	}
    
    	public void setOpenid(String openid) {
    		this.openid = openid;
    	}
    
    	public String getNickname() {
    		return nickname;
    	}
    
    	public void setNickname(String nickname) {
    		this.nickname = nickname;
    	}
    
    	public int getSex() {
    		return sex;
    	}
    
    	public void setSex(int sex) {
    		this.sex = sex;
    	}
    
    	public String getLanguage() {
    		return language;
    	}
    
    	public void setLanguage(String language) {
    		this.language = language;
    	}
    
    	public String getCity() {
    		return city;
    	}
    
    	public void setCity(String city) {
    		this.city = city;
    	}
    
    	public String getProvince() {
    		return province;
    	}
    
    	public void setProvince(String province) {
    		this.province = province;
    	}
    
    	public String getCountry() {
    		return country;
    	}
    
    	public void setCountry(String country) {
    		this.country = country;
    	}
    
    	public String getHeadimgurl() {
    		return headimgurl;
    	}
    
    	public void setHeadimgurl(String headimgurl) {
    		this.headimgurl = headimgurl;
    	}
    
    	public long getSubscribe_time() {
    		return subscribe_time;
    	}
    
    	public void setSubscribe_time(long subscribe_time) {
    		this.subscribe_time = subscribe_time;
    	}
    
    	public String getUnionid() {
    		return unionid;
    	}
    
    	public void setUnionid(String unionid) {
    		this.unionid = unionid;
    	}
    
    	public String getRemark() {
    		return remark;
    	}
    
    	public void setRemark(String remark) {
    		this.remark = remark;
    	}
    
    	public long getGroupid() {
    		return groupid;
    	}
    
    	public void setGroupid(long groupid) {
    		this.groupid = groupid;
    	}
    
    	@Override
    	public String toString() {
    		return "WechatUser{" +
    				"name='" + name + '\'' +
    				", subscribe=" + subscribe +
    				", openid='" + openid + '\'' +
    				", nickname='" + nickname + '\'' +
    				", sex=" + sex +
    				", language='" + language + '\'' +
    				", city='" + city + '\'' +
    				", province='" + province + '\'' +
    				", country='" + country + '\'' +
    				", headimgurl='" + headimgurl + '\'' +
    				", subscribe_time=" + subscribe_time +
    				", unionid='" + unionid + '\'' +
    				", remark='" + remark + '\'' +
    				", groupid=" + groupid +
    				'}';
    	}
    }
    
    

    发送http请求的HttpUtil获取accessToken的AccessTokenUtiljson字符串到对象的转换

    参考文献

    微信官方文档–网页授权

    微信官方文档–获取用户基本信息(UnionID机制)

    微信公众号开发–自定义菜单跳转页面并获取用户信息

    微信公众号开发–自定义菜单失败40033解决方案

    微信公众号开发–获取用户信息中文乱码的解决方案

    展开全文
  • 微信公众号开发教程(四)自定义菜单

    千次阅读 2018-06-22 14:19:09
    作者:陈惠,叩丁狼教育...菜单效果:打开开发文档,选择”自定义菜单”的”菜单创建接口”。注意: 1、自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单。 2、一级菜单最多4个汉字,二级菜单最多...
  • 开发者获取使用凭证(如何获取凭证)后,可以使用该凭证对公众账号的自定义菜单进行创建、查询和删除等操作。 自定义菜单接口可实现以下类型按钮: click(点击事件): 用户点击click类型按钮后,微信服务器会...
  • 微信公众号开发--自定义菜单跳转页面并获取用户信息 续
  • 本文是 微信开发-素材/消息管理接口 的后续,主要介绍微信公众平台的自定义菜单接口开发。由于个人的订阅号是没有大多数接口的权限的,所以我们需要使用微信官方提供的测试号来进行开发。测试号的申请可参考下文: ...
  • 微信开发自定义菜单

    千次阅读 2017-07-12 14:19:42
    微信开发的过程中,我们发现有很多的权限我们个人的订阅号是没有的,所以呢,我们采用测试号来进行开发。测试号有它自己的ID和密码,有自己的URL绑定地址以及绑定域名的地方,注意绑定域名不要加http://,只包括自己...
  • 1、由于需要对接微信开发的时候只能通过文档中的接口去创建菜单 打开自定义菜单接口文档地址(https://developers.weixin.qq.com/doc/offiaccount/Custom_Menus/Creating_Custom-Defined_Menu.html),我使用的是...
  • 微信公众平台开发模式自定义菜单接口API指南  开发实现方法,请查看  微信公众平台开发(58)自定义菜单   简介 开发者获取使用凭证(如何获取凭证)后,可以使用该凭证对公众账号的自定义菜单进行创建、...
  • (一)微信公众号开发之VS远程调试 (二)微信公众号开发之基础梳理 (三)微信公众号开发之自动消息回复和自定义菜单 前言 上一篇我们大致讲解了下微信公众号开发的基本原理和流程概述。本章主要是对文本消息...
  • 微信开发自定义菜单整套流程)

    万次阅读 2016-10-04 15:44:39
    一、自定义菜单概述 自定义菜单能够帮助公众号丰富界面,让用户更好更快地理解公众号的功能。开启自定义菜单后,公众号界面如图所示: 二、申请自定义菜单 个人订阅号使用微博认证、企业订阅号通过...
  • 微信公众平台服务号以及之前成功申请内测资格的订阅号都具有自定义菜单的功能。开发者可利用该功能为公众账号的会话界面底部增加自定义菜单,用户点击菜单中的选项,可以调出相应的回复信息或网页链接。自定义菜单...
  • 之前做了个自定义菜单的click事件,但是订阅号升级为服务号,相应的有了网页的基础授权(需要认证的服务号) 其中使用到了自定义菜单接口:...
  • 微信公众平台开发模式自定义菜单接口API指南  开发实现方法,请查看  微信公众平台开发(58)自定义菜单   简介 开发者获取使用凭证(如何获取凭证)后,可以使用该凭证对公众账号的自定义菜单进行创建、...
  • 请注意,创建自定义菜单后,由于微信客户端缓存,需要24小时微信客户端才会展现出来。建议测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果。 目前自定义菜单接口可实现两种类型按钮,如下: ...
  • 【wechat】微信开发——自定义菜单

    千次阅读 2018-06-28 09:57:43
    1.无后台管理的自定义菜单  ①根据appId和appSecret获取acessToken  a.向服务器发送请求 httpRequst(url,method,outPutStr) 返回jsonObject(包含了access_token和expires_in字段值)getString(""),ge
  • 微信小程序:自定义导航栏

    千次阅读 2019-06-17 19:29:56
    其实这是微信开放的自定义自定义导航栏来完成,但是最开始,对于一个页面很多的小程序,其实有点一言难尽,因为你自定义,你可能要所有页面都要添加一遍。 现在小程序可以自定义页面的json,你可以修改自定义某一个...
1 2 3 4 5 ... 20
收藏数 5,372
精华内容 2,148
关键字:

微信开发 获取当前自定义菜单