微信公众号开发入门

Posted 左直拳

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微信公众号开发入门相关的知识,希望对你有一定的参考价值。

实在是太折腾,太难懂了。也太坑了。

下面是这几天的,有关微信公众号的工作总结。算不上全面,只是我作为一个初哥的记录,仅以备忘。

一、微信公众号开发,开发什么?

公众号与小程序不同,小程序类似手机APP,微信只是提供了一个入口而已,而公众号则基本上是在微信框架内。微信公众号本质上是用户的一个联系人,只不过是比较特殊的联系人而已。通过微信提供的管理后台,无须任何编程,就可以很快搭建出一个像模像样的公众号,菜单、自动客服,文章更新。

当然,如果要更强的功能,就需要额外的服务了。比如,自动客服。通过公众号管理后台,可以定义一些自动回复语,但毕竟不够智能,这时我们可以在互联网上搭建服务器,提供相应服务。启用这个选项后,则公众号收到的用户的消息,全部由我们的服务来处理,原有的自动客服失效。当然,提供服务,要有网址、域名。

除此之外,还有菜单。菜单项可以设置为点击则回复一些消息,或跳转到小程序,或打开一张页面。未经验证的公众号,只能打开发表在当前公众号里的文章或图片、视频之类,而经过验证的公众号,点击菜单项可以直接打开网址。这个网址,应该没有什么限制,但我觉得,通常都会是部署在互联网上的所谓微信网页,即这些个页面不是普通的网页,而是使用了微信JS-SDK的页面,上面有各种微信的元素,比如扫一扫啦,分享到朋友圈啦,诸如此类。这些个页面,需要开发。

还有就是给关注者发送消息。我认为这是微信公众号最大的卖点。比如说,我关注了某个公众号,通过这个公众号的菜单,打开了相关小程序办事,事情有了进展,系统就可以通过这个公众号给我发个消息,提示一下当前进度。我认为这是公众号开发中最值得做的工作。

当然,还可以通过程序自动组织材料,在公众号上发表文章。不过这种事情,人工在公众号管理后台也能做,无非动动手而已。

二、微信公众号开发前铺垫

1、公众号的分类
衣穿三色,食分五等。所谓微信公众号,分为订阅号和服务号,个人只能申请订阅号,企业可以申请订阅号和服务号。然后公众号又有经过认证和未经认证之分。是否经过认证,在进行开发,调用微信服务上的差别非常大。未经认证,基本上没有啥可以玩的。而且!个人申请的订阅号,是没有微信认证这一说法的,直接堵死这扇门。

那开发怎么办?微信提供了测试账号这个机制,我们可以申请测试账号,用这个账号来测试微信服务接口。这个账号所有接口都能测,好像无敌了。然鹅!像微信网页这些,需要跑在手机上看效果,有些渲染测试账号是出不来的,比如所谓的微信开放标签(就是微信自己定义的,类似html的标签),用测试账号的信息注册页面功能,根本没东西显示出来。

总之,整个微信体系,感觉东西很多很杂,各种自定义的术语,对于初接触者,晕头转向。然后提供的开发文档,洋洋洒洒,密密麻麻,但你又往往看不出它说的究竟是个什么东西,只是一上来就说应该怎么怎么弄,却又极少举例子。这就给初学者带来很大的困扰,人为制造了许多的障碍。

订阅号与服务号侧重点不同。按我的理解,订阅号侧重发表文章,服务号则侧重针对性发通知消息。总的来说,服务号功能比订阅号要强大许多。

表面上看,订阅号每天可以群发1条消息,而服务号只能每个月发4条,订阅号要强。问题是,群发消息有啥用?我们网上办事,要的是针对本人的消息。这个消息,其实叫“模板消息”。只有服务号才能发送模板消息。这种消息,是根据需要,针对某个人进行发送。为啥叫模板消息呢?是因为这种消息发送时,要结合模板生成消息文本,然后进行发送。就像我们的手机短信。做过手机短信开发都知道,手机短信可不是随随便便就能发送的,因为众所周知的原因,怕有违法、出格的内容,所以手机短信管理后台,有所谓的模板,即短信格式是固定的,许多字眼也都是固定的,我们每次发送时只需往里面填一些内容就行。这个模板是要事先创建好并通过电信运营商审批。微信这个模板消息也一样,需要结合模板。

2、调试工具
就是微信开发者工具。注意是开发者工具,而不是开发工具。这个工具,对于小程序来说,的确就是一个开发工具;而对于公众号来说,只是一个调试工具,并不能通过它来键入什么代码;而且只是微信网页的调试工具。此时它只是一个微信浏览器。方法是在微信开发者工具的顶部输入微信网页地址即可浏览,调试,跟普通浏览器按下F12差不多。

3、查看运行结果
公众号通过微信客户端就能看到。这里说的查看结果,是指查看 微信网页 的运行结果。虽然有网页二字,但这不是普通的网页,用一般浏览器其实看不到什么东西,最好是通过微信浏览器或者微信开发者工具来运行。手机上并没有一个app叫微信浏览器,是微信隐含的。怎么召唤它呢?可以将微信网页的地址发给微信某个好友,比如“文件传输助手”,然后在聊天记录里点击这个网址,即用微信浏览器打开。绝逼是微信浏览器,QQ浏览器都不好使。

4、开发文档
基于微信做二次开发,网上查资料基本没什么卵用,最好还是老老实实地啃微信官方开发文档。

公众号开发文档在 公众号管理后台 - 设置与开发 - 开发者工具 - 开发文档 打开。

微信有两个平台,小程序的叫“微信开发开放平台”,公众号的叫“微信开发公众平台”。

5、一些术语

1)管理员及运营者
申请公众号的人是管理员。申请过程中,要申请人无数次用自己的微信去扫二维码。这对开发来说就比较尴尬,管理员一般是公众号的所有权人,但他往往不开发,那后台设置要做些修改咋办?难道下下都惊动大老板来扫二维码吗?可以把我们加到运营者名单里,我们自己扫码就行。运营者分长期和短期2种,应该是长期运营者,权限才够大。可在 微信公众号管理后台 - 设置与开发 - 人员设置 里设置。

2)IP白名单
在开发过程中,我们需要访问微信服务器,比如获取token之类。发出请求的IP,需要在白名单上。这个IP,是指互联网IP。假如我们在公司内部开发,那这个IP是公司上网的IP。问题是,这个IP经常变。我基本上是每天改一次白名单。

3)开发者微信号
公众号管理后台 - 设置与开发 - web开发者工具,将我们开发人员的微信号加进去。这是开发微信网页要用到的。因为微信开发者工具需要微信号登录。

4)JS接口安全域名
公众号管理后台 - 设置与开发 - 公众号设置 - 功能设置。

也是微信网页开发需要。我们的页面,需要放在这个域名里,微信的js-sdk才可以使用。

填写这个域名的时候,需要下载一个txt文件放到域名下,微信验证域名真伪才能保存。不过,我们在本地开发,可以修改host文件,将本机ip映射为这个域名。

5)微信开放标签
类似HTML的,微信独有的标签。如

跳转小程序:<wx-open-launch-weapp>
跳转App:<wx-open-launch-app>
服务号订阅通知:<wx-open-subscribe>
音频播放:<wx-open-audio>

三、微信网页开发

1、概述
微信网页真的就是一张张网页,只不过,它引用了微信提供的JS库,可能使用微信独有的,类似html的所谓开放标签。并且这个微信网页似乎在普通浏览器上运行,虽不会报错,但似乎看不到什么效果,只能运行在微信浏览器或微信开发者工具上。

以下是一张微信网页(spring boot下,结合了thymeleaf)

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <title>wechat</title>
    <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
    <script src="./libs/jquery.min.js"></script>
    <style>
        .sao
            text-align: center;width: 100%;height:5.5em;background-color: #ddd;line-height: 5.5em;
            cursor:pointer;
        
        .block
            height: 100px;
            border:solid 1px red;
        
    </style>
</head>

<body>
	<!-- 在手机微信上打开本页面,点这个扫一扫,真的会打开摄像头 -->
    <div class="sao qr_btn">扫一扫</div>
    
    <div class="block">
        <!-- 微信开放标签。测试号不支持,只有认证服务号才可以 -->
        <wx-open-subscribe th:template="$template" id="subscribe-btn">
            <script type="text/wxtag-template" slot="style">
                <style>
                    .subscribe-btn 
                        color: #fff;
                        background-color: #07c160;
                    
                </style>
            </script>
            <script type="text/wxtag-template">
                <button class="subscribe-btn">
                    模版消息订阅
                </button>
            </script>
        </wx-open-subscribe>
    </div>
</body>

<script th:inline="javascript">
	//微信验证
    wx.config(
        debug: true,
        appId: /*[[$wc.appId]]*/'',
        timestamp: /*[[$wc.timestamp]]*/'',
        nonceStr: /*[[$wc.nonceStr]]*/'',//随机串
        signature: /*[[$wc.signature]]*/'',
        jsApiList: ['chooseImage','scanQRCode','updateAppMessageShareData','updateTimelineShareData'],//需要使用的微信js-sdk函数列表
        openTagList: ['wx-open-subscribe']//开放标签列表
    );

    wx.ready(function () 
        // 微信分享 -- 分享给朋友
        wx.updateAppMessageShareData(
            title: '分享给您的猪朋狗友吧',
            desc: '独食难肥',
            link: location.href.split('#')[0],
            imgUrl: "http://test.duduchuhai.com/images/share.png",
            success: function(res) 
                console.log(res);
            
        );

        // 微信分享 -- 分享到朋友圈
        wx.updateTimelineShareData(
            title: '分享到猪圈',
            link: location.href.split('#')[0],
            imgUrl: "http://test.duduchuhai.com/images/share.png",
            success: function(res) 
                console.log(res);
            
        );
    );

    wx.error(function (res) 
        console.log(res);      // res为微信返回的错误结果
    );

    // 微信扫一扫
    $(".qr_btn").on('click', function() 
        wx.scanQRCode(
            needResult: 0, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
            scanType: ["qrCode","barCode"], // 可以指定扫二维码还是一维码,默认二者都有
            success: function (res) 
                var result = res.resultStr; // 当needResult 为 1 时,扫码返回的结果
            
        );
    );
</script>
<script>
    var btn = document.getElementById('subscribe-btn');
    console.log(btn);
    btn.addEventListener('success', function (e) 
        console.log('success', e.detail);
    );
    btn.addEventListener('error',function (e) 
        console.log('fail', e.detail);
    );
</script>
</html>

2、验证
微信网页运行时,首先要经过微信验证,然后才能正常使用微信各种功能。验证流程是,
1)凭appId和appSecret访问微信服务器获得token
2)用token访问微信服务器获得ticket
3)依次用ticket、随机串、时间戳、当前页面地址组合成一个字符串,然后对字符串进行sha散列运算,得到一个摘要
4)用摘要访问微信服务器获得签名
5)将 appId、时间戳、随机串、签名、本页面需要使用的微信函数、微信开放标签注册到微信

上面例子中,

    wx.config(
        debug: true,
        appId: /*[[$wc.appId]]*/'',
        timestamp: /*[[$wc.timestamp]]*/'',//注意是秒,不是毫秒
        nonceStr: /*[[$wc.nonceStr]]*/'',//随机串
        signature: /*[[$wc.signature]]*/'',//关键所在
        jsApiList: ['chooseImage','scanQRCode','updateAppMessageShareData','updateTimelineShareData'],//需要使用的微信js-sdk函数列表
        openTagList: ['wx-open-subscribe']//开放标签列表
    );

为了得到这个json对象可不容易。appId在微信公众号管理后台可得到,每个公众号都有唯一一个appId;时间戳也容易得到;随机串是自己定的,相对比较容易;最麻烦的是这个签名signature,我调试了差不多1天,总是说非法签名。

获取这个json对象的java代码:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;

import javax.annotation.PostConstruct;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;

@Service
public class WxServiceImpl implements WxService 
    @Override
    public WxConfig getWxConfig(String url) //url是微信网页地址
        WxConfig wc = new WxConfig();//这个是自定义的对象,不必深究

        wc.setAppId(APPID);
        wc.setNonceStr(getNonceStr());//随机串
        wc.setTimestamp((long) (new Date()).getTime() / 1000);//时间戳
        wc.setSignature(getSignature(wc.getNonceStr(), wc.getTimestamp(), url));//签名

        return wc;
    

    @PostConstruct
    void init() 
    	/*
    		由于从微信服务器获取token和ticket的函数有调用次数限制(每天<=2000),因此用redis将它们缓存起来
    	*/
        this.jedis = new Jedis(redis的IP, redis端口号);
    

    private String getSignature(String nonceStr, long timestamp, String url) //获取签名
        String signature = null;

        String ticket = getTicket();
        if (ticket != null) 
            String string1 = String.format("jsapi_ticket=%s&noncestr=%s&timestamp=%d&url=%s",
                    ticket,
                    nonceStr,
                    timestamp,
                    url);
            signature = getSha1(string1);
        

        return signature;
    

	//redis对象
    private Jedis jedis;
	//除了redis缓存,也用静态变量保存一份。不过,应用程序重启它们就消失了,并且不会自动过期
	//而从微信获取到的token和ticket有效期是7200秒
    private String _ticket = null;
    private String _token = null;
	//锁。为避免并发,使用锁机制,不要大家都去获取token和ticket
    private ReentrantLock lockTok = new ReentrantLock();
    private ReentrantLock lockTik = new ReentrantLock();

    private String getTicket() 
        String ticket = null;

        String key = "ticket";
        ticket = getKey(key, this._ticket);
        if (ticket == null) 
            //锁定。
            lockTik.lock();
            ticket = getKey(key, this._ticket);//再努力一把
            if (ticket == null) 
                String token = getToken();
                if (token != null) 
                    ticket = callGet(String.format("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi",
                            token), "ticket");
                    if (ticket != null) 
                        this._ticket = ticket;
                        setKey(key, ticket);
                    
                
            
            //解锁
            lockTik.unlock();
        

        return ticket;
    
    private String getToken() 
        String token = null;

        String key = "token";
        token = getKey(key, this._token);
        if (token == null) 
            lockTok.lock();
            token = getKey(key, this._token);
            if (token == null) 
                token = callGet(String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",APPID,AppSecret), 
                "access_token");
                if (token != null) 
                    this._token = token;
                    setKey(key, token);
                
            
            lockTok.unlock();
        

        return token;
    
    final static int EXPIRTED = 7200;
    private String getKey(String key, String v) 
        String value = null;

        try 
            value = jedis.get(key);
         catch (Exception ex) 
            value = v;//如果无法从redis中读取则将候补变量值返回。但变量值可能有过期的问题
            System.err.println(ex.getMessage());
        

        return value;
    
    private void setKey(String key, String value) 
        try 
            jedis.set(key, value);
            jedis.expire(key, EXPIRTED);
         catch (Exception ex) 
            System.err.println(ex.getMessage());
        
    
    final static int NONCESTR = 16;//随机串的长度为16。这个数值是自己定的
    private String getNonceStr() // 生成随机字符串noncestr
        String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        StringBuffer noncestr = new StringBuffer();
        int limit = chars.微信公众号开发--入门

微信公众号及小程序开发入门

微信公众号及小程序开发入门

微信公众平台开发入门

微信小程序开发入门

大数据开发入门--------微信公众号开通啦