2018-04-20 11:02:45 u012869196 阅读数 159
  • 微信公众平台应用开发(开发模式)

    微信公众平台开发教程,该课程内容包括本地调试:业务逻辑测试、消息接收与响应、测试流程;符号表情,微信开发模式,消息加解密等。 讲师:刘运强,网名柳峰,计算机硕士研究生,畅销书《微信公众平台应用开发:方法、技巧与案例》作者。 本课程主要面向有一定Java基础、对微信公众平台开发感兴趣的人员,能够帮助他们入门微信公众平台开发,少走弯路。

    8592 人正在学习 去看看 CSDN讲师

微信开发三大平台

微信公众平台、微信小程序、微信开放平台(第三方平台)
微信开放平台:可以创建移动应用、网站应用、第三方平台,需要缴纳认证费300元/年
微信公众平台:可以创建公众号、小程序,需要缴纳认证费300元/年

微信公众平台

https://mp.weixin.qq.com/
微信公众平台是运营者通过公众号为微信用户提供资讯和服务的平台,而公众平台开发接口则是提供服务的基础,开发者在公众平台网站中创建公众号、获取接口权限。

微信小程序

https://mp.weixin.qq.com/
微信小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验。

微信开放平台

https://open.weixin.qq.com/
公众平台第三方平台是为了让公众号或小程序运营者,在面向垂直行业需求时,可以一键授权给第三方平台(并且可以同时授权给多家第三方),通过第三方平台来完成业务,开放给所有通过开发者资质认证后的开发者使用。
在技术上,第三方平台是通过获得公众号或小程序的接口能力的授权,然后代公众平台帐号调用各业务接口来实现业务的。

注意事项

为了识别用户,每个用户针对每个公众号会产生一个安全的OpenID,如果需要在多公众号、移动应用之间做用户共通,则需前往微信开放平台,将这些公众号和应用绑定到一个开放平台账号下,绑定后,一个用户虽然对多个公众号和应用有多个不同的OpenID,但他对所有这些同一开放平台账号下的公众号和应用,只有一个UnionID,可以在用户管理-获取用户基本信息(UnionID机制)文档了解详情。

2018-12-29 17:00:57 liqing0013 阅读数 235
  • 微信公众平台应用开发(开发模式)

    微信公众平台开发教程,该课程内容包括本地调试:业务逻辑测试、消息接收与响应、测试流程;符号表情,微信开发模式,消息加解密等。 讲师:刘运强,网名柳峰,计算机硕士研究生,畅销书《微信公众平台应用开发:方法、技巧与案例》作者。 本课程主要面向有一定Java基础、对微信公众平台开发感兴趣的人员,能够帮助他们入门微信公众平台开发,少走弯路。

    8592 人正在学习 去看看 CSDN讲师

申请账号

  • 注册微信开发者账号(查看微信小程序官网
  • 购买阿里云服务器、域名,并完成备案。不是很复杂,但是比较耗时。可以参考其他博客。
  • 在阿里云服务器安装 Nginx 并配置 SSL。可以参考这里
  • 把阿里云服务器配置为微信小程序的服务器

下载并安装开发者工具

  • 官网下载即可(略)

创建第一个小程序项目

  • 运行开发者工具,使用默认模板创建即可(略)
  • 博主的小程序项目是通过 “建立云开发快速启动模板” 创建的(据说无需搭建服务器,就可以使用云端能力,但是博主前面还是配置了服务器)

指定环境——部署Login云函数

  • 点击工具栏的“云开发”,在弹出窗口点击“开通”,并自定义环境名。
    • 在这里插入图片描述
    • 在这里插入图片描述
    • 在这里插入图片描述
  • 回到开发者工具,选中cloudfunction,右键选中“同步云函数列表”
    • 在这里插入图片描述
    • 在这里插入图片描述
  • 右键 login 文件夹,选择“创建并部署”
    • 在这里插入图片描述
    • 在这里插入图片描述

安装 wx-server-sdk

  • 发现 login 函数调用异常,缺少 sdk:
    • 在这里插入图片描述
  • 回到开发者工具,右键 login 文件夹,选择“在终端中打开”
    • 运行命令:npm install --save wx-server-sdk@latest
    • 在这里插入图片描述
  • 右键 login 文件夹,选择“上传并部署”
  • 最后,再进行一次编译
    • 在这里插入图片描述

测试欢迎页的功能

  • 测试获取 openId 功能:
    • 在这里插入图片描述
    • 在这里插入图片描述

小结

  • 一次微信小程序的初体验到此结束。
  • 上面的有些步骤在当前看来是多余的,但是后期肯定是必要的。等博主更加熟悉微信小程序开发之后再来补上。
2019-11-25 18:02:47 YuanZhea 阅读数 83
  • 微信公众平台应用开发(开发模式)

    微信公众平台开发教程,该课程内容包括本地调试:业务逻辑测试、消息接收与响应、测试流程;符号表情,微信开发模式,消息加解密等。 讲师:刘运强,网名柳峰,计算机硕士研究生,畅销书《微信公众平台应用开发:方法、技巧与案例》作者。 本课程主要面向有一定Java基础、对微信公众平台开发感兴趣的人员,能够帮助他们入门微信公众平台开发,少走弯路。

    8592 人正在学习 去看看 CSDN讲师

概念

微信公众平台-第三方平台(简称第三方平台)开放给所有通过开发者资质认证后的开发者使用。在得到公众号或小程序运营者(简称运营者)授权后,第三方平台开发者可以通过调用微信开放平台的接口能力,为公众号或小程序的运营者提供账号申请、小程序创建、技术开发、行业方案、活动营销、插件能力等全方位服务。同一个账号的运营者可以选择多家适合自己的第三方为其提供产品能力或委托运营。

写此篇博客的缘由

由于腾讯文档的极其简陋,导致很多开发者(我)多走了许多弯路。所以我立下誓言如果我这边开发完成,我一定搞篇博客,给后来者指条路,因为腾讯实在太坑了,废话就不啰嗦了,直接开讲。

第一步:创建第三方平台

1).在微信开放平台-管理中心-第三方平台中创建第三方平台账号。创建第三方平台

2).选择“平台型服务商类型”创建第三方平台。填写第三方平台的基本信息. 填写基本信息

3).设置相关权限信息,具体权限集信息可参考:第三方平台权限说明
4).填写开发资料的信息说明, 可参考:第三方平台申请资料说明

第二步:调试开发

      接收第三方验证票据(component_verity_ticket

在第三方平台创建审核通过后,微信服务器会向其“授权事件接收URL”每隔10分钟定时推送component_verify_ticket。第三方平台方在收到ticket推送后也需进行解密,接收到后必须直接返回字符串success。

/**
     * 用于接受微信传来的ticket
     *
     * @return string
     */
    public function authCallback(){

        $xml_msg=file_get_contents('php://input');

        $msg=array(
            "timeStamp"  => empty($_GET['timestamp'])     ? ""    : trim($_GET['timestamp']) ,
            "nonce"     => empty($_GET['nonce'])     ? ""    : trim($_GET['nonce']) ,
            "msg_sign"   => empty($_GET['msg_signature']) ? ""    : trim($_GET['msg_signature'])
        );
       //第三方平台的配置信息
        $wx_settins=[
   
               'app_id'  => env('WECHAT_OPEN_PLATFORM_APPID', ''),
               'secret'  => env('WECHAT_OPEN_PLATFORM_SECRET', ''),
               'token'   => env('WECHAT_OPEN_PLATFORM_TOKEN', ''),
               'aes_key' => env('WECHAT_OPEN_PLATFORM_AES_KEY', ''),
       ];

        $result = $this->component_decode($xml_msg,$msg,$wx_settins);
        $ticket = WechatTicket::where('ticket', $result)->get();
        if(!count($ticket)){
            $Data=[
                'appid'=>open_platform_app_id,
                'ticket'=>$result,
                'created_at'=>date('Y-m-d H:i:s')
            ];
            WechatTicket::insert($Data);
        }
        return 'success';
    }

注意1:component_verity_ticket建议每次接受都进行写入缓存/数据库/文件

注意2:微信发送的请求中总共有5个参数,具体如下:

时间戳 timestamp,随机数nonce , encrypt_type(加密类型,为aes)和msg_signature(消息体签名,用于验证消息体的正确性)以及xml内容 

XML内容

<xml>

<AppId></AppId>

<CreateTime>1413192605 </CreateTime>

<InfoType> </InfoType>

<ComponentVerifyTicket> </ComponentVerifyTicket>

</xml>

注意3:对上述XML内容解密

解密/加密方式都是aes,

需要在创建第三方平台时填写开发资料时填写的:消息验证token,消息加解密key,appid

和微信请求来的参数:msg_signature(签名),timestamp(时间戳),nonce(随机数),postDataStr(post来的数据字符串),进行校验和解密成明文内容。然后提取出ComponentVerifyTicket进行写入缓存/数据库/文件(后续所有的操作都需要用到)

       获取令牌(component_access_token

第三方平台component_access_token是第三方平台的下文中接口的调用凭据,也叫做令牌(component_access_token)。每个令牌是存在有效期(2小时)的,且令牌的调用不是无限制的,请第三方平台做好令牌的管理,在令牌快过期时(比如1小时30分)再进行刷新.

  /**
     * 获取component_access_token
     *
     * @return bool|string
     */
    public function getComponentAccessToken()
    {
        $componentAccessToken=Redis::get('component_access_token');
        if(!$componentAccessToken){
            // 获取最新的ticket
            $ticket = WechatTicket::where('appid', $this->open_platform['open_platform']['app_id'])
                ->orderBy('created_at', 'DESC')
                ->limit(1)
                ->select(['ticket'])
                ->first();
            $this->componentVerifyTicket = $ticket->ticket;
            $url = "https://api.weixin.qq.com/cgi-bin/component/api_component_token";
            $postData = array(
                "component_appid" =>open_platform_app_id,
                "component_appsecret" => open_platform_secret,
                "component_verify_ticket" => $this->componentVerifyTicket
            );
            $componentAccessTokenReq = $this->http($url, $postData, 'POST');
            $componentAccessTokenArr = json_decode($componentAccessTokenReq, true);
            $componentAccessToken = $componentAccessTokenArr['component_access_token'];
            Redis::setex('component_access_token', 5400, $componentAccessToken); //有效时间2个小时,缓存一定要小于两个小时
        }
        return $componentAccessToken;
    }
  1. 判断token未过期直接返回(不要频繁的请求token,微信有请求次数限制)
  2. 用最新的ticket
  3. 拼装请求数据
  4. 发送Https请求并获取结果
  5. 解析结果并缓存token

       获取预授权码(pre_auth_code

该API用于获取预授权码。预授权码用于公众号或小程序授权时的第三方平台方安全验证

/**
     * 获取预授权码
     *
     * @param string $componentAccessToken
     * @return bool|string
     */
    public function getPreAuthCode(string $componentAccessToken)
    {
        $url = "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=" . $componentAccessToken;
        $postData = array(
            "component_appid" => $open_platform_app_id, //第三方平台的APPID
        );

        return OpenWechat::newInstance()->http($url, $postData, 'POST');//发送post请求
    }

重定向到授权页,引入用户进入授权页

第三方平台方可以在自己的网站:中放置“微信公众号授权”的入口,引导公众号进入授权页。授权页网址为

https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=xxxx&pre_auth_code=xxxxx&redirect_uri=xxxx

该网址中第三方平台方需要提供第三方平台方appid、预授权码和回调URI(授权成功后直接跳转的页面),

步骤:

  • 在第三方平台指定的域名下做一个简单的页面作为授权页的入口
  • 获取pre_auth_code
  • 替换重定向的url的参数

   

 /**
     * 公众号授权
     *
     * @param Request $request
     * @return string
     */
    public function openOauth(Request $request){
        // 1、获取component_access_token
        $componentAccessToken= OpenWechat::getComponentAccessToken();
        if($request->has('auth_code')){
            // 存在auth_code 说明前两个接口已请求过
            // 4、使用授权码换取公众号或小程序的接口调用凭据和授权信息
            $authorization = $this->getAuthToken($componentAccessToken,$request->input('auth_code'));
            $authorization_info = json_decode($authorization,true);
            $whereData=['authorizer_appid'=>$authorization_info['authorization_info']['authorizer_appid']];
            $updataData=[
                'authorizer_access_token'=>$authorization_info['authorization_info']['authorizer_access_token'],
                'authorizer_refresh_token'=>$authorization_info['authorization_info']['authorizer_refresh_token']
            ];
            WechatAccount::updateOrCreate($whereData,$updataData);
        }else{
            // 2、获取预授权码
            $preAuthCodeReq = $this->getPreAuthCode($componentAccessToken);
            $preAuthCodeArr = json_decode($preAuthCodeReq,true);
            $preAuthCode = $preAuthCodeArr['pre_auth_code'];

            // 3、引导进入授权页面
            $url = "https://mp.weixin.qq.com/cgi-bin/componentloginpage?component_appid=". $this->open_platform['open_platform']['app_id'] ."&pre_auth_code=". $preAuthCode ."&redirect_uri=".$request->fullUrl();
            return view('wechat.auth', [
                'url' => $url,
            ]);
        }

        return 'success';
    }

注意:接收授权方授权请求并一定要保存授权方信息

在第四步后重定向到授权页后,在授权页上会有一个二维码,微信公众号管理员(开发者的权限不行)通过的使用本人微信扫描二维码对第三方平台进行授权,在公众号管理员扫描二维码后,第三方平台后台给定的回调地址(redirect_uri将会收到一条请求,请求中包含了授权方的authorization_code和authorization_code的有效期 ;

  1. 使用authorization_code换取授权方的authorizer_access_token和authorizer_refresh_token。
​
/**
     * 获取公众号信息包括token refresh_token
     *
     * @param string $componentAccessToken
     * @param string $authCode
     * @return bool|string
     */
    private function getAuthToken(string $componentAccessToken, string $authCode)
    {
        $url = "https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token=" . $componentAccessToken;
        $postData = array(
            'component_appid' => , //第三方app_id
            "authorization_code" => $authCode
        );
//        return $this->http($url, $postData, 'POST');
        return OpenWechat::newInstance()->http($url, $postData, 'POST');
    }

​

步骤:

  • 获取第三方平台的token
  • 获取授权方授权信息
  • 获取授权方账号信息进行保存

获取/刷新接口调用令牌

在公众号/小程序接口调用令牌(authorizer_access_token)失效时,可以使用刷新令牌(authorizer_refresh_token)获取新的接口调用令牌。

注意: authorizer_access_token 有效期为 2 小时,开发者需要缓存 authorizer_access_token,避免获取/刷新接口调用令牌的 API 调用触发每日限额

   /**
     * 更新authorizer_access_token
     *
     * @return bool|string
     */
    public function UpdateAuthorizerAccessToken($authorizer_appid)
    {
        $component_access_token=$this->getComponentAccessToken();
        $old_authorizer_refresh_token= WechatAccount::where("authorizer_appid",$authorizer_appid)->first(['authorizer_refresh_token']);
        $url="https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token=$component_access_token";
        $postData = array(
            "component_appid" => $this->open_platform['open_platform']['app_id'],
            "authorizer_appid" => $authorizer_appid,
            "authorizer_refresh_token" => $old_authorizer_refresh_token->authorizer_refresh_token
        );
        $authorizer_refresh_token=$this->http($url, $postData, 'POST');
        $new_authorizer_refresh_token=json_decode($authorizer_refresh_token, true);
        $whereData=['authorizer_appid'=>$authorizer_appid];
        $updataData=[
            'authorizer_access_token'=>$new_authorizer_refresh_token['authorizer_access_token'],
            'authorizer_refresh_token'=>$new_authorizer_refresh_token['authorizer_refresh_token']
        ];
        WechatAccount::updateOrCreate($whereData,$updataData);
    }

公用的http请求方法

/**
     * post 请求
     *
     * @param string $url
     * @param array $data
     * @param string $method
     * @return bool|string
     */
    public function http(string $url, array $data, string $method)
    {
        $data = json_encode($data);
        $curl = curl_init(); // 启动一个CURL会话
        curl_setopt($curl, CURLOPT_URL, $url); // 要访问的地址
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // 对认证证书来源的检查
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); // 从证书中检查SSL加密算法是否存在
        curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // 模拟用户使用的浏览器
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1); // 使用自动跳转
        curl_setopt($curl, CURLOPT_AUTOREFERER, 1); // 自动设置Referer
        if ($method == 'POST') {
            curl_setopt($curl, CURLOPT_POST, 1); // 发送一个常规的Post请求
            if ($data != '') {
                curl_setopt($curl, CURLOPT_POSTFIELDS, $data); // Post提交的数据包
            }
        }
        curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 设置超时限制防止死循环
        curl_setopt($curl, CURLOPT_HEADER, 0); // 显示返回的Header区域内容
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 获取的信息以文件流的形式返回
        $tmpInfo = curl_exec($curl); // 执行操作
        curl_close($curl); // 关闭CURL会话
        return $tmpInfo; // 返回数据
    }

第三步:代公众号调用接口

 当微信公众号授权给第三方开发平台后,第三方开发平台执行某些操作时,如查询用户信息、发送模板消息等等,这就需要使用authorizer_access_token。这个token从获得开始,2小时内有效,如果需要继续使用授权,就需要在有效期内主动刷新token。但是当某些原因导致刷新token失败时,仍然可以使用authorizer_refresh_token(自获得起30天内有效)重新获得authorizer_access_token。这里的authorizer_access_token就可以代替普通的access_token去调你授权的接口了。

 

2016-06-16 11:34:55 w410589502 阅读数 12251
  • 微信公众平台应用开发(开发模式)

    微信公众平台开发教程,该课程内容包括本地调试:业务逻辑测试、消息接收与响应、测试流程;符号表情,微信开发模式,消息加解密等。 讲师:刘运强,网名柳峰,计算机硕士研究生,畅销书《微信公众平台应用开发:方法、技巧与案例》作者。 本课程主要面向有一定Java基础、对微信公众平台开发感兴趣的人员,能够帮助他们入门微信公众平台开发,少走弯路。

    8592 人正在学习 去看看 CSDN讲师

注:文章代码来源于柳峰的微信公众平台应用开发

微信开发公众平台自定义菜单需要花钱认证才能实现,不想花钱只能玩测试账号了,不过这并不影响开发。我的开发都是基于柳峰老师的微信公众平台应用开发做的。

只要我们使用公众平台测试账号就可以开发自定义菜单了,比较方便,测试账号开放了很多接口,很方便。

在开发自定义菜单的时候可以参考微信公众平台开发者文档的自定义菜单创建。

一、自定义菜单

1、自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单。
2、一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“...”代替。
3、创建自定义菜单后,菜单的刷新策略是,在用户进入公众号会话页或公众号profile页时,如果发现上一次拉取菜单的请求在5分钟以前,就会拉取一下菜单,如果菜单有更新,就会刷新客户端的菜单。测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果。

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

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
二、访问自定义菜单接口

1:获取access_token

  自定义菜单的创建、查询和删除需要调用公众平台开放的自定义菜单接口,而调用该接口需要先获取access_token(接口访问凭证),这些接口全都是基于https协议的,因此我们先要解决如何在java程序中发送https请求的问题。

  获取接口访问凭证access_token:


获取access_token是通过GET方式访问如下链接:
<span style="font-size:12px;">https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET</span>

链接中有三个参数,分别是grant_type、appid和secret。根据图中的参数说明,grant_type传固定值client_credential,而appid和secret就是申请完自定义菜单后微信分配给我们的。

请求发送成功后,微信服务器会返回一个json串,包含access_token和expires_in两个元素。其中,access_token就是我们最终需要的凭证,而expires_in是凭证的有效期,单位是秒,7200秒也就是2个小时。这就意味着,不是每次访问特殊接口,都需要重新获取一次access_token,只要access_token还在有效期内,就一直可以使用。

2:菜单创建

创建菜单要调用菜单接口

接口调用请求说明

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

其实就是向地址https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN以POST方式提交一个JSON格式的菜单字符串。

三、封装通用请求方法(自定义信任管理器)

创建菜单需要两个接口,一个是获取access_token接口,一个是自定义菜单接口,都是https请求。

1:创建证书信任管理器

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

package org.liufeng.weixin.util;  
  
import java.security.cert.CertificateException;  
import java.security.cert.X509Certificate;  
  
import javax.net.ssl.X509TrustManager;  
  
/** 
 * 证书信任管理器(用于https请求) 
 *  
 * @author liufeng 
 * @date 2013-08-08 
 */  
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;  
    }  
} 
证书管理器的作用就是信任指定所有证书。
2:创建通用https请求

通用https请求应该

1)支持HTTPS请求;

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

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

package org.liufeng.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.JSONObject;  
  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
  
/** 
 * 公众平台通用接口工具类 
 *  
 * @author liuyq 
 * @date 2013-08-09 
 */  
public class WeixinUtil {  
    private static Logger log = LoggerFactory.getLogger(WeixinUtil.class);  
  
    /** 
     * 发起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;  
    }  
}  


四、封装菜单实体类

  1:按钮的分类

  我们通常会把自定义菜单项看做是按钮,按钮的类型分成click(点击事件)和view(访问网页)。

  click类型的按钮有type、name和key3个属性,而view类型的按钮有type、name和url3个属性

  

  2:首先是调用获取凭证接口后,微信服务器会返回json格式的数据:{"access_token":"ACCESS_TOKEN","expires_in":7200},我们将其封装为一个AccessToken对象,对象有二个属性:token和expiresIn,代码如下:

package org.liufeng.weixin.pojo;  
  
/** 
 * 微信通用接口凭证 
 *  
 * @author liufeng 
 * @date 2013-08-08 
 */  
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;  
    }  
}  

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

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"
            }]
       }]
 }
3:封装菜单结构

每个按钮对象都要一个共同的name属性,因此需要定义一个按钮对象基类,所有的按钮对象都需要继承该类。基类的代码如下:

package org.liufeng.weixin.pojo;  
  
/** 
 * 按钮的基类 
 *  
 * @author liufeng 
 * @date 2013-08-08 
 */  
public class Button {  
    private String name;  
  
    public String getName() {  
        return name;  
    }  
  
    public void setName(String name) {  
        this.name = name;  
    }  
}

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

package org.liufeng.weixin.pojo;  
  
/** 
 * 普通按钮(子按钮) 
 *  
 * @author liufeng 
 * @date 2013-08-08 
 */  
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;  
    }  
}  

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

package org.liufeng.weixin.pojo;  
  
/** 
 * 复杂按钮(父按钮) 
 *  
 * @author liufeng 
 * @date 2013-08-08 
 */  
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;  
    }  
} 

对整个菜单进行封装,菜单对象包含多个菜单项(最多只能有3个),这些菜单项即可以是子菜单项(不含二级菜单的一级菜单),也可以是父菜单项(包含二级菜单的菜单项)

package org.liufeng.weixin.pojo;  
  
/** 
 * 菜单 
 *  
 * @author liufeng 
 * @date 2013-08-08 
 */  
public class Menu {  
    private Button[] button;  
  
    public Button[] getButton() {  
        return button;  
    }  
  
    public void setButton(Button[] button) {  
        this.button = button;  
    }  
}  
这样我们就完成了菜单实体类的封装。


凭证access_token的获取方法

继续在先前通用请求方法的类WeixinUtil.java中加入以下代码,用于获取接口访问凭证:


// 获取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";  
  
/** 
 * 获取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;  
} 

自定义菜单的创建方法

继续在先前通用请求方法的类WeixinUtil.java中加入以下代码,用于创建自定义菜单:

// 菜单创建(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;  
}  

调用封装的方法创建自定义菜单

 

package org.liufeng.weixin.main;  
  
import org.liufeng.weixin.pojo.AccessToken;  
import org.liufeng.weixin.pojo.Button;  
import org.liufeng.weixin.pojo.CommonButton;  
import org.liufeng.weixin.pojo.ComplexButton;  
import org.liufeng.weixin.pojo.Menu;  
import org.liufeng.weixin.util.WeixinUtil;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
  
/** 
 * 菜单管理器类 
 *  
 * @author liufeng 
 * @date 2013-08-08 
 */  
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");  
  
        ComplexButton mainBtn1 = new ComplexButton();  
        mainBtn1.setName("生活助手");  
        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 });  
  
        /** 
         * 这是公众号xiaoqrobot目前的菜单结构,每个一级菜单都有二级菜单项<br> 
         *  
         * 在某个一级菜单下没有二级菜单的情况,menu该如何定义呢?<br> 
         * 比如,第三个一级菜单项不是“更多体验”,而直接是“幽默笑话”,那么menu应该这样定义:<br> 
         * menu.setButton(new Button[] { mainBtn1, mainBtn2, btn33 }); 
         */  
        Menu menu = new Menu();  
        menu.setButton(new Button[] { mainBtn1, mainBtn2, mainBtn3 });  
  
        return menu;  
    }  
}

注意:在运行以上代码时,需要将appId和appSecret换成你自己公众号的。


响应菜单点击事件

package org.liufeng.course.service;  
  
import java.util.Date;  
import java.util.Map;  
  
import javax.servlet.http.HttpServletRequest;  
  
import org.liufeng.course.message.resp.TextMessage;  
import org.liufeng.course.util.MessageUtil;  
  
/** 
 * 核心服务类 
 *  
 * @author liufeng 
 * @date 2013-05-20 
 */  
public class CoreService {  
    /** 
     * 处理微信发来的请求 
     *  
     * @param request 
     * @return 
     */  
    public static String processRequest(HttpServletRequest request) {  
        String respMessage = null;  
        try {  
            // 默认返回的文本消息内容  
            String respContent = "请求处理异常,请稍候尝试!";  
  
            // xml请求解析  
            Map<String, String> requestMap = MessageUtil.parseXml(request);  
  
            // 发送方帐号(open_id)  
            String fromUserName = requestMap.get("FromUserName");  
            // 公众帐号  
            String toUserName = requestMap.get("ToUserName");  
            // 消息类型  
            String msgType = requestMap.get("MsgType");  
  
            // 回复文本消息  
            TextMessage textMessage = new TextMessage();  
            textMessage.setToUserName(fromUserName);  
            textMessage.setFromUserName(toUserName);  
            textMessage.setCreateTime(new Date().getTime());  
            textMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);  
            textMessage.setFuncFlag(0);  
  
            // 文本消息  
            if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {  
                respContent = "您发送的是文本消息!";  
            }  
            // 图片消息  
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE)) {  
                respContent = "您发送的是图片消息!";  
            }  
            // 地理位置消息  
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LOCATION)) {  
                respContent = "您发送的是地理位置消息!";  
            }  
            // 链接消息  
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LINK)) {  
                respContent = "您发送的是链接消息!";  
            }  
            // 音频消息  
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_VOICE)) {  
                respContent = "您发送的是音频消息!";  
            }  
            // 事件推送  
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {  
                // 事件类型  
                String eventType = requestMap.get("Event");  
                // 订阅  
                if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {  
                    respContent = "谢谢您的关注!";  
                }  
                // 取消订阅  
                else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) {  
                    // TODO 取消订阅后用户再收不到公众号发送的消息,因此不需要回复消息  
                }  
                // 自定义菜单点击事件  
                else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {  
                    // 事件KEY值,与创建自定义菜单时指定的KEY值对应  
                    String eventKey = requestMap.get("EventKey");  
  
                    if (eventKey.equals("11")) {  
                        respContent = "天气预报菜单项被点击!";  
                    } else if (eventKey.equals("12")) {  
                        respContent = "公交查询菜单项被点击!";  
                    } else if (eventKey.equals("13")) {  
                        respContent = "周边搜索菜单项被点击!";  
                    } else if (eventKey.equals("14")) {  
                        respContent = "历史上的今天菜单项被点击!";  
                    } else if (eventKey.equals("21")) {  
                        respContent = "歌曲点播菜单项被点击!";  
                    } else if (eventKey.equals("22")) {  
                        respContent = "经典游戏菜单项被点击!";  
                    } else if (eventKey.equals("23")) {  
                        respContent = "美女电台菜单项被点击!";  
                    } else if (eventKey.equals("24")) {  
                        respContent = "人脸识别菜单项被点击!";  
                    } else if (eventKey.equals("25")) {  
                        respContent = "聊天唠嗑菜单项被点击!";  
                    } else if (eventKey.equals("31")) {  
                        respContent = "Q友圈菜单项被点击!";  
                    } else if (eventKey.equals("32")) {  
                        respContent = "电影排行榜菜单项被点击!";  
                    } else if (eventKey.equals("33")) {  
                        respContent = "幽默笑话菜单项被点击!";  
                    }  
                }  
            }  
  
            textMessage.setContent(respContent);  
            respMessage = MessageUtil.textMessageToXml(textMessage);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
  
        return respMessage;  
    }  
} <span style="color:#3333ff;"> </span>





2018-03-21 19:10:46 PersevereStudy 阅读数 398
  • 微信公众平台应用开发(开发模式)

    微信公众平台开发教程,该课程内容包括本地调试:业务逻辑测试、消息接收与响应、测试流程;符号表情,微信开发模式,消息加解密等。 讲师:刘运强,网名柳峰,计算机硕士研究生,畅销书《微信公众平台应用开发:方法、技巧与案例》作者。 本课程主要面向有一定Java基础、对微信公众平台开发感兴趣的人员,能够帮助他们入门微信公众平台开发,少走弯路。

    8592 人正在学习 去看看 CSDN讲师

2018年,随着微信小程序的不断更新,小程序可以支持更加丰富的应用场景,这或许也将使小程序像5年前的微信公众号一样,成为下一个风口。

本次小程序主要实现经常刷爆朋友圈的输入姓名测星座运势的小程序。

前期源代码

下载安装微信小程序官方开发软件:微信web开发者工具,这个可以搜索下载安装。然后就扫码,填写小程序App-id,创建项目,就可以在微信web开发者工具写源代码了。



微信小程序主要包括三个页面:

app.js(javascript,小游戏的主体,同时也是小程序的逻辑实现)

app.wxml(weixin Mark Language,微信标记语言)

app.wxss(weixin Style Sheet,微信样式表)

基本每一个页面都需要这三个脚本,需要其他页面,按照这样的方式写源代码就可以了。


UI素材

这个要会一点PS,把小程序界面捣鼓得美观一些。


调试

不断地测试,修改BUG,最后确认无误上传源代码,


小程序代码提交腾讯后,审核了整整三天,终于通知审核通过。


还有最后一步发布:【逢考我必过】小程序




附上同名微信公众号:逢考我必过,专注分享教育、考试相关信息,妈妈再也不用担心我的学习了!快来关注我吧!


微信小程序开发

阅读数 85

微信小程序

阅读数 16

没有更多推荐了,返回首页