java微信订阅号(公众号)开发案例
微信公众号开发一般是针对企业和组织的,个人一般只能申请订阅号,并且调用的接口有限,下面我们就来简单的描述下接入公众号的步骤:
1、首先你需要一个邮箱在微信公众号平台进行注册;
注册的方式有订阅号、公众号、小程序和企业号,个人我们这里只能选择订阅号
2、注册完后,我们登录到公众号平台--->开发--->基本配置,这里需要填写URL和token,URL就是我们使用服务器的接口;
3、java web服务器程序编译好且在服务器上部署可以运行的话,可在微信公众号进行在线接口调试:
1)、选择合适的接口
2)、系统会生成该接口的参数表,您可以直接在文本框内填入对应的参数值(红色星号表示该字段必填)
3)、点击检查问题按钮,即可得到相应的调试信息
eg:获取access_token的步骤:
1)、接口类型:基础支持
2)、接口列表:获取access_token接口/token
3)、填写相应的参数:grant_type、appid、secret
4)、点击检查问题
5)、验证成功会返回结果和提示,结果为:200 ok,提示:Request successful即表示验证成功
我们这里验证比较多的是消息接口调试:文本消息、图片消息、语音消息、视频消息、etc
4、接口有不理解的地方,可进入开发-->开发者工具-->开发者文档进行查询
5、接口权限:订阅号主要有基础支持、接收消息及网页服务里面的一些接口
下面我们主要讲订阅号怎么样接收消息的案例:
1、需要申请一个个人微信订阅号
2、url服务器和服务器端代码部署(可以用腾讯云or阿里云服务器)
1)、AccountsServlet.java类,验证来自微信服务器和微信服务器的消息处理
package cn.jon.wechat.servlet;import java.io.IOException;
import java.io.PrintWriter;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import cn.jon.wechat.service.AccountsService;
import cn.jon.wechat.utils.SignUtil;public class AccountsServlet extends HttpServlet {public AccountsServlet() {super();}public void destroy() {super.destroy(); // Put your code here}/*** 确认请求来自于微信服务器*/public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {System.out.println("接口测试开始!!!");//微信加密签名String signature = request.getParameter("signature");//时间戳String timestamp = request.getParameter("timestamp");//随机数String nonce = request.getParameter("nonce");//随机字符串String echostr = request.getParameter("echostr");PrintWriter out = response.getWriter();//通过校验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败if(SignUtil.checkSignature(signature,timestamp,nonce)){out.print(echostr);}out.close();out = null;
// response.encodeRedirectURL("success.jsp");}/*** 处理微信服务器发来的消息*/public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {//消息的接受、处理、响应request.setCharacterEncoding("utf-8");response.setCharacterEncoding("utf-8");//调用核心业务类型接受消息、处理消息String respMessage = AccountsService.processRequest(request);//响应消息PrintWriter out = response.getWriter();out.print(respMessage);out.close();}public void init() throws ServletException {// Put your code here}}
2)、SignUtil.java类,请求校验工具类,token需要和微信中填写的token一致
package cn.jon.wechat.utils;import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;/*** 请求校验工具类* @author jon*/
public class SignUtil {//与微信配置中的的Token一致private static String token = "";public static boolean checkSignature(String signature, String timestamp,String nonce) {String[] arra = new String[]{token,timestamp,nonce};//将signature,timestamp,nonce组成数组进行字典排序Arrays.sort(arra);StringBuilder sb = new StringBuilder();for(int i=0;i<arra.length;i++){sb.append(arra[i]);}MessageDigest md = null;String stnStr = null;try {md = MessageDigest.getInstance("SHA-1");byte[] digest = md.digest(sb.toString().getBytes());stnStr = byteToStr(digest);} catch (NoSuchAlgorithmException e) {// TODO Auto-generated catch blocke.printStackTrace();}//释放内存sb = null;//将sha1加密后的字符串与signature对比,标识该请求来源于微信return stnStr!=null?stnStr.equals(signature.toUpperCase()):false;}/*** 将字节数组转换成十六进制字符串* @param digestArra* @return*/private static String byteToStr(byte[] digestArra) {// TODO Auto-generated method stubString digestStr = "";for(int i=0;i<digestArra.length;i++){digestStr += byteToHexStr(digestArra[i]);}return digestStr;}/*** 将字节转换成为十六进制字符串*/private static String byteToHexStr(byte dByte) {// TODO Auto-generated method stubchar[] Digit = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};char[] tmpArr = new char[2];tmpArr[0] = Digit[(dByte>>>4)&0X0F];tmpArr[1] = Digit[dByte&0X0F];String s = new String(tmpArr);return s;}public static void main(String[] args) {/*byte dByte = 'A';System.out.println(byteToHexStr(dByte));*/Map<String,String> map = new ConcurrentHashMap<String, String>();map.put("4", "zhangsan");map.put("100", "lisi");Set set = map.keySet();Iterator iter = set.iterator();while(iter.hasNext()){
// String keyV = (String) iter.next();String key =(String)iter.next();System.out.println(map.get(key));
// System.out.println(map.get(iter.next()));}/*for(int i=0;i<map.size();i++){}*/}
}
3)、AccountsService.java服务类,主要是消息的请求和响应处理,并且当用户关注你的公众号的时候,可以设置默认推送消息
package cn.jon.wechat.service;import java.util.Date;
import java.util.Map;import javax.servlet.http.HttpServletRequest;import cn.jon.wechat.message.req.ImageMessage;
import cn.jon.wechat.message.req.LinkMessage;
import cn.jon.wechat.message.req.LocationMessage;
import cn.jon.wechat.message.req.VideoMessage;
import cn.jon.wechat.message.req.VoiceMessage;
import cn.jon.wechat.message.resp.TextMessage;
import cn.jon.wechat.utils.MessageUtil;/*** 解耦,使控制层与业务逻辑层分离开来,主要处理请求,响应* @author jon*/
public class AccountsService {public static String processRequest(HttpServletRequest request) {String respMessage = null;//默认返回的文本消息内容String respContent = "请求处理异常,请稍后尝试!";try {//xml请求解析Map<String,String> requestMap = MessageUtil.pareXml(request);//发送方账号(open_id)String fromUserName = requestMap.get("FromUserName");//公众账号String toUserName = requestMap.get("ToUserName");//消息类型String msgType = requestMap.get("MsgType");//默认回复此文本消息 TextMessage defaultTextMessage = new TextMessage(); defaultTextMessage.setToUserName(fromUserName); defaultTextMessage.setFromUserName(toUserName); defaultTextMessage.setCreateTime(new Date().getTime()); defaultTextMessage.setMsgType(MessageUtil.MESSSAGE_TYPE_TEXT); defaultTextMessage.setFuncFlag(0); // 由于href属性值必须用双引号引起,这与字符串本身的双引号冲突,所以要转义 defaultTextMessage.setContent("欢迎访问<a href=\"http://blog.csdn.net/j086924\">jon的博客</a>!");
// defaultTextMessage.setContent(getMainMenu());// 将文本消息对象转换成xml字符串 respMessage = MessageUtil.textMessageToXml(defaultTextMessage); //文本消息if(msgType.equals(MessageUtil.MESSSAGE_TYPE_TEXT)){//respContent = "Hi,您发送的是文本消息!";//回复文本消息TextMessage textMessage = new TextMessage();
// textMessage.setToUserName(toUserName);
// textMessage.setFromUserName(fromUserName);//这里需要注意,否则无法回复消息给用户了textMessage.setToUserName(fromUserName);textMessage.setFromUserName(toUserName);textMessage.setCreateTime(new Date().getTime());textMessage.setMsgType(MessageUtil.MESSSAGE_TYPE_TEXT);textMessage.setFuncFlag(0);respContent = "Hi,你发的消息是:"+requestMap.get("Content");textMessage.setContent(respContent);respMessage = MessageUtil.textMessageToXml(textMessage);}//图片消息else if(msgType.equals(MessageUtil.MESSSAGE_TYPE_IMAGE)){ImageMessage imageMessage=new ImageMessage();imageMessage.setToUserName(fromUserName);imageMessage.setFromUserName(toUserName);imageMessage.setCreateTime(new Date().getTime());imageMessage.setMsgType(MessageUtil.MESSSAGE_TYPE_IMAGE);//respContent=requestMap.get("PicUrl");imageMessage.setPicUrl("http://img24.pplive.cn//2013//07//24//12103112092_230X306.jpg");respMessage = MessageUtil.imageMessageToXml(imageMessage);}//地理位置else if(msgType.equals(MessageUtil.MESSSAGE_TYPE_LOCATION)){LocationMessage locationMessage=new LocationMessage();locationMessage.setToUserName(fromUserName);locationMessage.setFromUserName(toUserName);locationMessage.setCreateTime(new Date().getTime());locationMessage.setMsgType(MessageUtil.MESSSAGE_TYPE_LOCATION);locationMessage.setLocation_X(requestMap.get("Location_X"));locationMessage.setLocation_Y(requestMap.get("Location_Y"));locationMessage.setScale(requestMap.get("Scale"));locationMessage.setLabel(requestMap.get("Label"));respMessage = MessageUtil.locationMessageToXml(locationMessage);}//视频消息else if(msgType.equals(MessageUtil.MESSSAGE_TYPE_VIDEO)){VideoMessage videoMessage=new VideoMessage();videoMessage.setToUserName(fromUserName);videoMessage.setFromUserName(toUserName);videoMessage.setCreateTime(new Date().getTime());videoMessage.setMsgType(MessageUtil.MESSSAGE_TYPE_VIDEO);videoMessage.setMediaId(requestMap.get("MediaId"));videoMessage.setThumbMediaId(requestMap.get("ThumbMediaId"));respMessage = MessageUtil.videoMessageToXml(videoMessage);}//链接消息else if(msgType.equals(MessageUtil.MESSSAGE_TYPE_LINK)){LinkMessage linkMessage=new LinkMessage();linkMessage.setToUserName(fromUserName);linkMessage.setFromUserName(toUserName);linkMessage.setCreateTime(new Date().getTime());linkMessage.setMsgType(MessageUtil.MESSSAGE_TYPE_LINK);linkMessage.setTitle(requestMap.get("Title"));linkMessage.setDescription(requestMap.get("Description"));linkMessage.setUrl(requestMap.get("Url"));respMessage = MessageUtil.linkMessageToXml(linkMessage);}//语音消息else if(msgType.equals(MessageUtil.MESSSAGE_TYPE_VOICE)){VoiceMessage voiceMessage=new VoiceMessage();voiceMessage.setToUserName(fromUserName);voiceMessage.setFromUserName(toUserName);voiceMessage.setCreateTime(new Date().getTime());voiceMessage.setMsgType(MessageUtil.MESSSAGE_TYPE_VOICE);voiceMessage.setMediaId(requestMap.get("MediaId"));voiceMessage.setFormat(requestMap.get("Format"));respMessage = MessageUtil.voiceMessageToXml(voiceMessage);}//事件推送else if(msgType.equals(MessageUtil.MESSSAGE_TYPE_EVENT)){//事件类型String eventType = requestMap.get("Event");//订阅if(eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)){respContent = "谢谢关注!";}//取消订阅else if(eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)){//System.out.println("取消订阅");}else if(eventType.equals(MessageUtil.EVENT_TYPE_CLICK)){//自定义菜单消息处理System.out.println("自定义菜单消息处理");}}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}return respMessage;}public static String getMainMenu(){StringBuffer buffer =new StringBuffer();buffer.append("您好,我是jon,请回复数字选择服务:").append("\n");buffer.append("1、我的博客").append("\n");buffer.append("2、 歌曲点播").append("\n");buffer.append("3、 经典游戏").append("\n");buffer.append("4 、聊天打牌").append("\n\n");buffer.append("回复"+"0"+"显示帮助菜单");return buffer.toString();}
}
4)、MessageUtil.java帮助类,包括常量的定义和xml消息转换和处理
package cn.jon.wechat.utils;import java.io.InputStream;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;import javax.servlet.http.HttpServletRequest;import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import cn.jon.wechat.message.req.ImageMessage;
import cn.jon.wechat.message.req.LinkMessage;
import cn.jon.wechat.message.req.LocationMessage;
import cn.jon.wechat.message.req.VideoMessage;
import cn.jon.wechat.message.req.VoiceMessage;
import cn.jon.wechat.message.resp.Article;
import cn.jon.wechat.message.resp.MusicMessage;
import cn.jon.wechat.message.resp.NewsMessage;
import cn.jon.wechat.message.resp.TextMessage;import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;/*** 各种消息的处理类 * @author jon*/public class MessageUtil {/*** 文本类型*/public static final String MESSSAGE_TYPE_TEXT = "text";/*** 音乐类型*/public static final String MESSSAGE_TYPE_MUSIC = "music";/*** 图文类型*/public static final String MESSSAGE_TYPE_NEWS = "news";/*** 视频类型*/public static final String MESSSAGE_TYPE_VIDEO = "video";/*** 图片类型*/public static final String MESSSAGE_TYPE_IMAGE = "image";/*** 链接类型*/public static final String MESSSAGE_TYPE_LINK = "link";/*** 地理位置类型*/public static final String MESSSAGE_TYPE_LOCATION = "location";/*** 音频类型*/public static final String MESSSAGE_TYPE_VOICE = "voice";/*** 推送类型*/public static final String MESSSAGE_TYPE_EVENT = "event";/*** 事件类型:subscribe(订阅)*/public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";/*** 事件类型:unsubscribe(取消订阅)*/public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";/*** 事件类型:click(自定义菜单点击事件)*/public static final String EVENT_TYPE_CLICK= "CLICK";/*** 解析微信发来的请求 XML */@SuppressWarnings("unchecked")public static Map<String,String> pareXml(HttpServletRequest request) throws Exception {//将解析的结果存储在HashMap中Map<String,String> reqMap = new HashMap<String, String>();//从request中取得输入流InputStream inputStream = request.getInputStream();//读取输入流SAXReader reader = new SAXReader();Document document = reader.read(inputStream);//得到xml根元素Element root = document.getRootElement();//得到根元素的所有子节点List<Element> elementList = root.elements();//遍历所有的子节点取得信息类容for(Element elem:elementList){reqMap.put(elem.getName(),elem.getText());}//释放资源inputStream.close();inputStream = null;return reqMap; }/*** 响应消息转换成xml返回* 文本对象转换成xml*/public static String textMessageToXml(TextMessage textMessage) {xstream.alias("xml", textMessage.getClass());return xstream.toXML(textMessage);}/*** 语音对象的转换成xml* */public static String voiceMessageToXml(VoiceMessage voiceMessage) {xstream.alias("xml", voiceMessage.getClass());return xstream.toXML(voiceMessage);}/*** 视频对象的转换成xml* */public static String videoMessageToXml(VideoMessage videoMessage) {xstream.alias("xml", videoMessage.getClass());return xstream.toXML(videoMessage);}/*** 音乐对象的转换成xml* */public static String musicMessageToXml(MusicMessage musicMessage) {xstream.alias("xml", musicMessage.getClass());return xstream.toXML(musicMessage);}/*** 图文对象转换成xml* */public static String newsMessageToXml(NewsMessage newsMessage) {xstream.alias("xml", newsMessage.getClass());xstream.alias("item", new Article().getClass());return xstream.toXML(newsMessage);}/*** 图片对象转换成xml**/public static String imageMessageToXml(ImageMessage imageMessage){xstream.alias("xml",imageMessage.getClass());return xstream.toXML(imageMessage);}/*** 链接对象转换成xml**/public static String linkMessageToXml(LinkMessage linkMessage){xstream.alias("xml",linkMessage.getClass());return xstream.toXML(linkMessage);}/*** 地理位置对象转换成xml**/public static String locationMessageToXml(LocationMessage locationMessage){xstream.alias("xml",locationMessage.getClass());return xstream.toXML(locationMessage);}/*** 拓展xstream,使得支持CDATA块* */private static XStream xstream = new XStream(new XppDriver(){public HierarchicalStreamWriter createWriter(Writer out){return new PrettyPrintWriter(out){//对所有的xml节点的转换都增加CDATA标记boolean cdata = true;@SuppressWarnings("unchecked")public void startNode(String name,Class clazz){super.startNode(name,clazz);}protected void writeText(QuickWriter writer,String text){if(cdata){writer.write("<![CDATA[");writer.write(text);writer.write("]]>");}else{writer.write(text);}}};}});}
5)、BaseMessage.java消息基类(包括:开发者微信号、用户账户、创建时间、消息类型、消息ID变量),文本、视频、图片消息都会继承此基类,在此基础上扩展自己的变量,可根据开发者文档中的各种消息属性进行定义
package cn.jon.wechat.message.req;
/*** 消息基类 (普通用户-公众号)* @author jon*/
public class BaseMessage {//开发者微信号private String ToUserName;//发送方账号(一个openId)private String FromUserName;//消息创建时间(整型)private long CreateTime;//消息类型(text/image/location/link...)private String MsgType;//消息id 64位整型private String MsgId;public BaseMessage() {super();// TODO Auto-generated constructor stub}public BaseMessage(String toUserName, String fromUserName, long createTime,String msgType, String msgId) {super();ToUserName = toUserName;FromUserName = fromUserName;CreateTime = createTime;MsgType = msgType;MsgId = msgId;}public String getToUserName() {return ToUserName;}public void setToUserName(String toUserName) {ToUserName = toUserName;}public String getFromUserName() {return FromUserName;}public void setFromUserName(String fromUserName) {FromUserName = fromUserName;}public long getCreateTime() {return CreateTime;}public void setCreateTime(long createTime) {CreateTime = createTime;}public String getMsgType() {return MsgType;}public void setMsgType(String msgType) {MsgType = msgType;}public String getMsgId() {return MsgId;}public void setMsgId(String msgId) {MsgId = msgId;}
}
6)、TextMessage.java文本消息,继承自5中基类,扩展内容属性
package cn.jon.wechat.message.req;
/*** 文本消息* @author jon*/
public class TextMessage extends BaseMessage{//消息内容private String content;public String getContent() {return content;}public void setContent(String content) {this.content = content;}}
7)、ImageMessage.java图片消息
package cn.jon.wechat.message.req;
/*** 图片消息* @author jon*/
public class ImageMessage extends BaseMessage{//图片链接private String PicUrl;public String getPicUrl() {return PicUrl;}public void setPicUrl(String picUrl) {PicUrl = picUrl;}
}
8)、VideoMessage.java视频消息
package cn.jon.wechat.message.req;public class VideoMessage extends BaseMessage {private String mediaId;private String thumbMediaId;public String getMediaId() {return mediaId;}public void setMediaId(String mediaId) {this.mediaId = mediaId;}public String getThumbMediaId() {return thumbMediaId;}public void setThumbMediaId(String thumbMediaId) {this.thumbMediaId = thumbMediaId;} }
其他消息类可根据开发者文档自行进行完成,另外,开发者也可以申请公众平台测试账号,对开发的相关内容进行测试
源代码下载地址
java微信订阅号(公众号)开发案例相关推荐
- 微信小程序公众号开发
微信小程序&公众号开发 一.什么是微信开发 二.微信开放平台 三.微信公众平台 四.小程序与公众号的区别 1. 用途不同 2. 运营方式不同 3. 操作方法不同 4. 用户体验不同(公众号操作 ...
- 微信开放平台 公众号第三方平台开发 教程一 平台介绍
教程导航: 微信开放平台 公众号第三方平台开发 教程一 平台介绍 微信开放平台 公众号第三方平台开发 教程二 创建公众号第三方平台 微信开放平台 公众号第三方平台开发 教程三 一键登录授权给第三方平台 ...
- 华为开发微信鸿蒙版,微信鸿蒙开发者公众号报名了
[其他] 微信鸿蒙开发者公众号报名了 169910 电梯直达 zhongshuidi 渐入佳境 发表于 2020-12-18 10:16:32 来自:HUAWEI Mate 30 Pro 5G 最新回 ...
- SAP系统和微信集成的系列教程之七:使用Redis存储微信用户和公众号的对话记录
这是Jerry 2020年的第88篇文章,也是汪子熙公众号总共第269篇原创文章. 本系列的英文版Jerry写作于2017年,这个教程总共包含十篇文章,发表在SAP社区上. 系列目录 (1) 微信开发 ...
- SAP系统和微信集成的系列教程之三:微信用户关注公众号之后,自动在SAP C4C系统创建客户主数据
这是Jerry 2020年的第84篇文章,也是汪子熙公众号总共第266篇原创文章. 本系列的英文版Jerry写作于2017年,这个教程总共包含十篇文章,发表在SAP社区上. 系列目录 (1) 微信开发 ...
- 公众号向特定用户主动推送消息_SAP系统和微信集成的系列教程之三:微信用户关注公众号之后,自动在SAP C4C系统创建客户主数据...
这是Jerry 2020年的第84篇文章,也是汪子熙公众号总共第266篇原创文章. 本系列的英文版Jerry写作于2017年,这个教程总共包含十篇文章,发表在SAP社区上: https://blogs ...
- php微信公众号支付实例教程,php微信支付之公众号支付功能
这篇文章主要为大家详细介绍了php微信支付之公众号支付功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 网上的很多PHP微信扫码支付接入教程都颇为复杂,且需要配置和引入较多的文件,本人通过整理后 ...
- 微信视频号直播功能上线;全面打通视频号+公众号+小商店+直播私域生态组合;丨国仁网络资讯
今年商业化脚步加快,动作频发的流量巨头微信终于放大招了,有部分小伙伴发现,,从10月2日起在视频号里发表新动态的旁边多了一个新功能--发起直播. 在全民直播,人人带货的当下,这个功能无疑将一石激起千层 ...
- 鸿蒙开发者微信公众号,微信鸿蒙开发者公众号报名了
[其他] 微信鸿蒙开发者公众号报名了 171110 电梯直达 zhongshuidi 渐入佳境 发表于 2020-12-18 10:16:32 来自:HUAWEI Mate 30 Pro 5G 最新回 ...
- java对接银联商务公众号+服务窗支付(1)
java实现银联商务公众号+服务窗对接----支付下单 GitLab地址:https://gitlab.com/982837387/UnionPayGetWay.git 本文对接银联商务公众号+服务窗 ...
最新文章
- python下载教程1001python下载教程-1001种玩法 | Python 学习指南资源
- 汇编语言笔记14-端口
- Vue中实现输入框Input输入格式限制
- Android访问WCF服务(使用json实现参数传递)
- jquery 判断元素显示或隐藏
- XGBoost的安装与介绍
- C++/OpenCV:error C4996: ‘fopen‘: This function or variable may be unsafe.
- C#中double.tostring()的用法
- vue 打包html静态页面,vue项目打包、vue项目打包后空白界面解决办法
- 使用maven官方仓库直接下载项目需要的jar包方法
- java经典算法(六)---zws
- 爬豆瓣读书Top250
- 解决nf_conntrack: table full, dropping packet
- 自燃、断轴、失控,新能源车还能买吗?
- suest:跨模型比较与广义豪斯曼检验
- 社会对计算机专业学生的需求,关于计算机专业社会人才需求调查报告
- Python经典编程习题100例:第20例:落体反弹问题
- SVN the working copy needs to be upgraded svn 解决办法
- 普通话测试软件分数准确吗,普通话考试容易过吗?
- yolov7利用onnx进行推理同时调用usb摄像头