开发简介
公众号的分类
我们平常在微信应用上会看到有很多的公众号,但是各自并不一样,公众号也分很多种类型,不过最常见的就是服务号和订阅号了。下面我们来看一下他们的区别:
-
订阅号
为媒体和个人提供一种信息传播方式,主要偏于为用户传达资讯(类似报纸杂志),主要的定位是阅读,每天可以群发1条消息; -
服务号
为企业,政府或组织提供对用户进行服务,主要偏于服务交互(类似银行提供服务查询),每个月只可群发4条消息; -
企业微信(企业号)
为企业,政府,事业单位,实现生产管理和协作运营的移动化,主要用于公司内部通讯使用,旨在为用户提供移动办公,需要先有成员的通讯信息验证才可以关注成功企业微信;
还有一个比较明显的区别就是,订阅号都是存放在一个名叫订阅号的文件夹中,点开才能看到所有关注过的订阅号,但是服务号却和好友一样直接显示在聊天列表中。这个大家打开微信客户端便能看到。
对于开发者来讲最明显的区别就是订阅号无法配置网页授权回调,使用此功能需要开通服务号
微信公众平台概述
微信公众平台是运营者通过公众号为微信用户提供资讯和服务的平台,而公众平台开发接口则是提供服务的基础为了识别用户,每个用户针对每个公众号会产生一个安全的OpenID,如果需要在多公众号、移动应用之间做用户共通,则需前往微信开放平台
微信公众平台和微信开放平台的区别
- 功能不同:
微信开放平台作为第三方移动程序提供接口,使用户可将第三方程序的内容发布给好友或分享至朋友圈,第三方内容借助微信平台获得更广泛的传播。利用公众账号平台进行自媒体活动,简单来说就是进行一对多的媒体性行为活动。
- 面向用户不同:
微信公众平台主要面向名人、政府、媒体、企业等机构推出的合作推广业务。在这里可以通过渠道将品牌推广给线上平台作用。微信开放平台面向第三方移动程序,为第三方移动程序提供接口。
- 接口类型不同:
微信公众平台提供的是微信端的消息推送、品牌传播、分享、功能定位、群发推送、自动回复、二维码订阅等接口类型。而微信开发平台提供的是第三方移动程序的接口类型,即接口代码可以集成到第三方的APP,在第三方的APP中使用微信的分享给微信好友、朋友圈的功能。
开发需知
-
在申请到认证公众号之前,你可以先通过测试号申请系统,快速申请一个接口测试号,立即开始接口测试开发。
-
在开发过程中,可以使用接口调试工具来在线调试某些接口。
-
每个接口都有每日接口调用频次限制,可以在公众平台官网-开发者中心处查看具体频次。
-
在开发出现问题时,可以通过接口调用的返回码,以及报警排查指引(在公众平台官网-开发者中心处可以设置接口报警),来发现和解决问题。
-
公众平台以
access_token
为接口调用凭据,来调用接口,所有接口的调用需要先获取access_token
,access_token
在2小时内有效,过期需要重新获取,但1天内获取次数有限,开发者需自行存储,详见获取接口调用凭据(access_token
)文档。 -
公众平台接口调用仅支持80端口。
开发模式
公众号主要通过公众号消息会话和公众号内网页来为用户提供服务的,下面分别介绍这两种情况:
1、公众号消息会话
公众号是以微信用户的一个联系人形式存在的,消息会话是公众号与用户交互的基础。目前公众号内主要有这样几类消息服务的类型,分别用于不同的场景。
-
群发消息:公众号可以以一定频次(订阅号为每天1次,服务号为每月4次),向用户群发消息,包括文字消息、图文消息、图片、视频、语音等。
-
被动回复消息:在用户给公众号发消息后,微信服务器会将消息发到开发者预先在开发者中心设置的服务器地址(开发者需要进行消息真实性验证),公众号可以在5秒内做出回复,可以回复一个消息,也可以回复命令告诉微信服务器这条消息暂不回复。被动回复消息可以设置加密(在公众平台官网的开发者中心处设置,设置后,按照消息加解密文档来进行处理。其他3种消息的调用因为是API调用而不是对请求的返回,所以不需要加解密)。
-
客服消息:在用户给公众号发消息后的48小时内,公众号可以给用户发送不限数量的消息,主要用于客服场景。用户的行为会触发事件推送,某些事件推送是支持公众号据此发送客服消息的,详见微信推送消息与事件说明文档。
-
模板消息:在需要对用户发送服务通知(如刷卡提醒、服务预约成功通知等)时,公众号可以用特定内容模板,主动向用户发送消息。
2、公众号内网页
许多复杂的业务场景,需要通过网页形式来提供服务,这时需要用到:
-
网页授权获取用户基本信息:通过该接口,可以获取用户的基本信息(获取用户的OpenID是无需用户同意的,获取用户的基本信息则需用户同意)
-
微信JS-SDK:是开发者在网页上通过JavaScript代码使用微信原生功能的工具包,开发者可以使用它在网页上录制和播放微信语音、监听微信分享、上传手机本地图片、拍照等许多能力。
开始开发
接入微信公众平台开发,开发者需要按照如下步骤完成:
- 填写服务器配置
① 公众平台官网登录之后,找到基本配置菜单栏
② 填写配置url,填写:http://服务地址/wx 。 http的端口号固定使用80,不可填写其他。 Token:自主设置,这个token
与公众平台wiki中常提的access_token
不是一回事。这个token
只用于验证开发者服务器。
- 验证服务器地址的有效性
- 微信验证服务器地址逻辑流程图
- 封装配置接口,响应微信请求
from django.http import HttpResponse
def handle(request):
# 微信验证
if request.method == 'GET':
if len(request.GET) == 0:
return HttpResponse('Hello, world')
# 请按照公众平台官网\基本配置中信息填写
token = 'hello520'
_str = ''.join(sorted([request.GET.get('timestamp', ''), request.GET.get('timestamp', ''), token]))
if hashlib.sha1(_str.encode('utf-8')).hexdigest() == request.GET.get('signature'):
return HttpResponse(request.GET.get('echostr'))
else:
return HttpResponse('Hello, world')
- 公众平台网页点击提交按钮。若提示”token验证失败”, 请认真检查代码或网络链接等。若token验证成功,会自动返回基本配置的主页面,点击启动按钮,即成功建立了连接
- 依据接口文档实现业务逻辑
公众号消息会话开发
配置完开发者服务器之后,当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。消息按类型分为文本消息、图片消息、语音消息、视频消息。在微信用户和公众号产生交互的过程中,用户的某些操作会使得微信服务器通过事件推送的形式通知到开发者在开发者中心处设置的服务器地址,从而开发者可以获取到该信息。其中,某些事件推送在发生后,是允许开发者回复用户的,某些则不允许,事件分别有: 关注/取消关注事件、 扫描带参数二维码事件、上报地理位置事件、自定义菜单事件、点击菜单拉取消息时的事件推送、点击菜单跳转链接时的事件推送、关注/取消关注事件
- 简单的处理会话消息
from wechatpy import parse_message
from six.moves.urllib.parse import quote
from wechatpy.replies import TextReply, ImageReply
from django.http import HttpResponse
def handle(request):
if request.method == 'POST':
msg = parse_message(request.body)
# 处理消息,文本类消息为text,图片类消息为image...
if msg.type == 'text':
# 获取文本消息为抽奖,推送抽奖超链接
reply = None
if msg.content == '抽奖':
content = '<a href="https://open.weixin.qq.com/connect/oauth2/authorize?' \
'appid={}&redirect_uri={}&response_type=code&scope=snsapi_userinfo&' \
'state=STATE#wechat_redirect">点此处抽奖</a>'.format(WX_CONFIG['AppId'],
quote(WX_CONFIG['uri'] + '/api/auth'))
reply = TextReply(
content=content,
message=msg)
# 获取文本消息为企业介绍,推送企业介绍图片
if msg.content == '企业介绍':
reply = ImageReply(
image='Ni3sYweVA3M3e43hrAoWGTPIkQw26TFQjaurGrpE',
message=msg
)
if reply:
r_xml = reply.render()
return HttpResponse(r_xml)
# 处理用户关注事件
if msg.event == 'subscribe':
pass
return HttpResponse('')
上面的代码中给用户推送了图片,用到了media_id
,那怎么才能获得media_id
呢。首先我们需要在微信公众平台网页或通过微信提供的素材上传接口上传素材到微信服务器,永久使用。新增的永久素材也可以在公众平台官网素材管理模块中查询管理。公众号新建的永久素材,可通过"获取素材列表"获知素材的media_id
。拿到media_id
后,就可以在和用户的交互中使用素材了。
上面代码我们在接口逻辑中回复了空串,为什么呢因为微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试。
不处理微信服务器的请求会出现什么呢?请看下图
公众号内网页授权
网页授权是什么呢,在日常生活中,我们扫码或者点击某个链接之后微信会弹出如下信息
这就是接下来要介绍的微信网页授权
- 公众平台配置网页授权
进入微信公众平台
- 码代码
- 文件接口
from django.http import HttpResponse
from django.urls import path
from rest_framework.decorators import api_view
@api_view(['GET'])
def wx_file(request):
""" 微信授权文件验证接口 """
return HttpResponse('R6M51GVKVhCgCJal')
urlpatterns = [
# 此接口放置在域名根目录下
path('MP_verify_R6M51GVKVhCgCJal.txt', wx_file, name='微信授权文件验证接口'),
]
- 回调接口
from rest_framework.decorators import api_view
from wechatpy.oauth import WeChatOAuth
from django.http import HttpResponse
from django.shortcuts import redirect
from core.settings import WX_CONFIG
from api.models import User
@api_view(['GET'])
def auth(request):
"""
微信网页授权 通过code换取网页授权access_token,
"""
# 微信认证服务实例化
oauth_client = WeChatOAuth(
app_id=WX_CONFIG['AppId'],
secret=WX_CONFIG['AppSecret'],
redirect_uri=WX_CONFIG['DOMAIN']
)
code = request.GET.get('code')
if not code:
return HttpResponse('')
res = oauth_client.fetch_access_token(code=code)
refresh_token = res.get('refresh_token')
# 检查access_token有效性,如果无效则返回4001,拿着refresh_token刷新access_token
if not oauth_client.check_access_token():
# 刷新 access_token
oauth_client.refresh_access_token(refresh_token)
# 利用access_token获取用户信息
user_info = oauth_client.get_user_info()
# 使用openid作为用户唯一识别标识
user = User.objects.filter(openid=user_info.get('openid')).first()
if not user:
user = User.objects.create(
openid=user_info.get('openid', ''),
nickname=user_info.get('nickname', ''),
headimgurl=user_info.get('headimgurl', '')
)
request.session['user_id'] = user.id
# 重定向到网页业务逻辑页,根据服务器保存用户信息处理业务逻辑
return redirect('/')
- 愉快地脱离微信开始网页开发(抽奖、答题、抽完奖答题、答完题抽奖…)
开发中遇到的问题
- 获取到微信用户昵称存在特殊字符及表情保存到数据库报错
解决办法:正则替换掉字符
import re
nickname = re.sub('[^\u4e00-\uafa5a-zA-Z0-9]', '', str(user_info.get('nickname', '')))