1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 微信公众号开发-素材/消息管理接口

微信公众号开发-素材/消息管理接口

时间:2019-09-10 15:52:21

相关推荐

微信公众号开发-素材/消息管理接口

开始

本文是 微信公众号开发者模式介绍及接入 的后续,如没看过前文的话,可能看本文会有些懵逼。本文主要介绍微信公众平台的素材、消息管理接口的开发。由于个人的订阅号是没有大多数接口的权限的,所以我们需要使用微信官方提供的测试号来进行开发。测试号的申请可参考下文:

使用微信测试账号对网页进行授权

图文消息

本小节我们来开发回复图文消息的功能,官方文档地址如下:

https://mp./wiki?t=resource/res_main&id=mp1421140543

回复图文消息所需传递的参数如下:

注:多图文消息不会显示Description参数的信息

官方的图文消息示例数据结构如下:

<xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[fromUser]]></FromUserName><CreateTime>12345678</CreateTime><MsgType><![CDATA[news]]></MsgType><ArticleCount>2</ArticleCount><Articles><item><Title><![CDATA[title1]]></Title><Description><![CDATA[description1]]></Description><PicUrl><![CDATA[picurl]]></PicUrl><Url><![CDATA[url]]></Url></item><item><Title><![CDATA[title]]></Title><Description><![CDATA[description]]></Description><PicUrl><![CDATA[picurl]]></PicUrl><Url><![CDATA[url]]></Url></item></Articles></xml>

图文消息都在Articles标签内,而每个item标签都包含一条图文消息,有多少个item标签就代表有多少条图文消息。

在开发回复图文消息的时候,我们需要使用到一张图片来作为图文消息的封面,找一个图片文件放在工程的resources/static目录下即可,并确保能够在外网上访问:

看完了官方的示例数据及文档,那么我们就来开发一下图文消息的回复吧。首先是创建一个基类,封装通用的字段,代码如下:

package org.zero01.weixin.mqdemo.vo;import com.thoughtworks.xstream.annotations.XStreamAlias;import lombok.Getter;import lombok.Setter;/*** @program: mq-demo* @description: 图文消息基类* @author: 01* @create: -07-02 20:24**/@Getter@Setterpublic class BaseMassage {/*** 接收方账号*/@XStreamAlias("ToUserName")private String toUserName;/*** 发送方账号*/@XStreamAlias("FromUserName")private String fromUserName;/*** 消息创建时间 (整型)*/@XStreamAlias("CreateTime")private long createTime;/*** 消息类型*/@XStreamAlias("MsgType")private String msgType;}

然后是具体的封装每条图文消息字段的对象,代码如下:

package org.zero01.weixin.mqdemo.vo;import com.thoughtworks.xstream.annotations.XStreamAlias;import lombok.Getter;import lombok.Setter;/*** @program: mq-demo* @description: 图文消息对象* @author: 01* @create: -07-02 20:19**/@Getter@Setterpublic class NewsItem{@XStreamAlias("Title")private String title;@XStreamAlias("Description")private String description;@XStreamAlias("PicUrl")private String picUrl;@XStreamAlias("Url")private String url;}

接着是包含每条图文消息的容器对象,代码如下:

package org.zero01.weixin.mqdemo.vo;import com.thoughtworks.xstream.annotations.XStreamAlias;import lombok.Getter;import lombok.Setter;import java.util.List;/*** @program: mq-demo* @description: 图文消息容器对象* @author: 01* @create: -07-02 20:29**/@Getter@Setterpublic class NewsMessage extends BaseMassage{@XStreamAlias("ArticleCount")private int articleCount;@XStreamAlias("Articles")private List<NewsItem> articles;}

将图文消息结构都封装成一个个的类后,就是需要组装图文消息以及将组装好的图文消息转换成xml格式的数据,发送给微信服务器了。所以我们需要在MessageUtil类中,新增如下两个方法:

/*** 图文消息转换为xml** @param newsMessage* @return*/public static String newsMessageToXml(NewsMessage newsMessage) {XStream xStream = new XStream();xStream.processAnnotations(new Class[]{NewsItem.class, NewsMessage.class});xStream.alias("xml", newsMessage.getClass());xStream.alias("item", NewsItem.class);return xStream.toXML(newsMessage);}/*** 组装图文消息** @param toUserName* @param fromUserName* @return*/public static String initNewsMessage(String toUserName, String fromUserName) {List<NewsItem> newsItemList = new ArrayList<>();NewsMessage newsMessage = new NewsMessage();NewsItem newsItem = new NewsItem();newsItem.setTitle("图文消息");newsItem.setDescription("这是一个图文消息");newsItem.setPicUrl("/code.jpg");newsItem.setUrl("");newsItemList.add(newsItem);newsMessage.setToUserName(fromUserName);newsMessage.setFromUserName(toUserName);newsMessage.setCreateTime(System.currentTimeMillis());newsMessage.setMsgType(MessageTypeEnum.MSG_NEWS.getMsgType());newsMessage.setArticles(newsItemList);newsMessage.setArticleCount(newsItemList.size());return newsMessageToXml(newsMessage);}

最后修改WeChatMqController中的text方法,增加一条判断,判断当用户输入数字1时,则回复图文消息。代码如下:

@PostMapping("/common")public String text(@RequestBody String xmlStr) {// 将xml格式的数据,转换为 AllMessage 对象AllMessage allMessage = MessageUtil.xmlToAllMessage(xmlStr);// 是否是文本消息类型if (allMessage.getMsgType().equals(MessageTypeEnum.MSG_TEXT.getMsgType())) {// 用户输入数字1时,回复图文消息if ("1".equals(allMessage.getContent())) {return MessageUtil.initNewsMessage(allMessage.getToUserName(), allMessage.getFromUserName());}// 自动回复用户所发送的文本消息return MessageUtil.autoReply(allMessage, ContentEnum.CONTENT_PREFIX.getContent() + allMessage.getContent());}// 是否是事件推送类型else if (allMessage.getMsgType().equals(MessageTypeEnum.MSG_EVENT.getMsgType())) {// 是否为订阅事件if (EventType.EVENT_SUBSCRIBE.getEventType().equals(allMessage.getEvent())) {// 自动回复欢迎语return MessageUtil.autoReply(allMessage, ContentEnum.CONTENT_SUBSCRIBE.getContent());}} else {// 暂不支持文本以外的消息回复return MessageUtil.autoReply(allMessage, ContentEnum.CONTENT_NONSUPPORT.getContent());}return MessageUtil.autoReply(allMessage, ContentEnum.CONTENT_NONSUPPORT.getContent());}

完成以上代码的编写后,启动SpringBoot,打开微信公众号,测试结果如下:

access_token的获取

本小节我们来看看如何获取access_token,官方文档地址如下:

https://mp./wiki?t=resource/res_main&id=mp1421140183

access_token是什么?官方的定义如下:

access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。

调用接口获取access_token需要传递的参数说明如下:

获取access_token成功后,接口所返回的参数说明如下:

从文档中我们可以看到,调用接口获取access_token时需要传递appid和secret,appid和secret可以在公众号的基本配置页面中获取,如下:

然后我们还需要安装提示,设置一下白名单的ip,即你机器的ip,不然是无法调用接口获取access_token的,如下:

将appid、secret以及获取access_token的接口url,配置到SpringBoot的配置文件中,如下:

wechat:mpAppid: wx8ed1xxxxxx9513ddmpAppSecret: 0c1b5b7ea5xxxxxxxxx14cb5b61258accessTokenUrl: https://api./cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

在工程中新建一个config包,在该包下新建一个 WeXinConfig 配置类,用于加载配置文件中所配置的appid和secret:

package org.zero01.weixin.mqdemo.config;import lombok.Data;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.context.annotation.Configuration;/*** @program: mq-demo* @description: 微信公众号配置类* @author: 01* @create: -07-03 20:50**/@Data@Configuration@ConfigurationProperties(prefix = "wechat")public class WeXinConfig {private String mpAppid;private String mpAppSecret;private String accessTokenUrl;}

因为我们需要序列化json数据以及发送http请求给微信服务器,所以需要使用到一些工具包,在maven的pom.xml文件中,加入如下依赖:

<!-- /artifact/org.json/json --><dependency><groupId>org.json</groupId><artifactId>json</artifactId><version>0810</version></dependency><!-- /artifact/org.apache.httpcomponents/httpclient --><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.5</version></dependency>

在util包下新建一个 WeiXinUtil 工具类,在该类中封装get、post请求方法,以及获取access_token的方法。代码如下:

package org.zero01.weixin.mqdemo.util;import org.apache.http.HttpResponse;import org.apache.http.client.methods.HttpGet;import org.apache.http.client.methods.HttpPost;import org.apache.http.entity.StringEntity;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClientBuilder;import org.apache.http.util.EntityUtils;import org.json.JSONObject;import org.springframework.beans.factory.annotation.Autowired;import org.ponent;import org.zero01.weixin.mqdemo.config.WeXinConfig;import org.zero01.weixin.mqdemo.vo.AccessToken;import java.io.IOException;/*** @program: mq-demo* @description: * @author: 01* @create: -07-03 21:04**/@Componentpublic class WeiXinUtil {private static WeXinConfig wxConfig;public WeXinConfig getWeXinConfig() {return wxConfig;}@Autowiredpublic void setWeXinConfig(WeXinConfig wxConfig) {WeiXinUtil.wxConfig = wxConfig;}/*** get请求** @param url* @return*/public static JSONObject doGet(String url) throws IOException {CloseableHttpClient client = HttpClientBuilder.create().build();HttpGet httpGet = new HttpGet(url);HttpResponse response = client.execute(httpGet);String result = EntityUtils.toString(response.getEntity(), "utf-8");return new JSONObject(result);}/*** post请求** @param url* @param outStr* @return*/public static JSONObject doPost(String url, String outStr) throws IOException {CloseableHttpClient client = HttpClientBuilder.create().build();HttpPost httpPost = new HttpPost(url);httpPost.setEntity(new StringEntity(outStr, "utf-8"));HttpResponse response = client.execute(httpPost);String result = EntityUtils.toString(response.getEntity(), "utf-8");return new JSONObject(result);}/*** 获取access_token** @return* @throws IOException*/public static AccessToken getAccessToken() throws IOException {AccessToken token = new AccessToken();// 替换appid和secretString url = wxConfig.getAccessTokenUrl().replace("APPID", wxConfig.getMpAppid()).replace("APPSECRET", wxConfig.getMpAppSecret());JSONObject jsonObject = doGet(url);token.setToken(jsonObject.getString("access_token"));token.setExpiresIn(jsonObject.getInt("expires_in"));return token;}}

完成以上代码的编写后,新建一个测试类,测试一下是否能正常获取到access_token。测试代码如下:

package org.zero01.weixin.mqdemo.util;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import org.zero01.weixin.mqdemo.vo.AccessToken;import java.io.IOException;import static org.junit.Assert.*;@RunWith(SpringRunner.class)@SpringBootTestpublic class WeiXinUtilTest {@Testpublic void getAccessToken() throws IOException {AccessToken accessToken = WeiXinUtil.getAccessToken();System.out.println("access_token: " + accessToken.getToken());System.out.println("有效时间: " + accessToken.getExpiresIn());}}

运行以上测试用例后,控制台输出如下:

access_token: 11_AMxhxO9soXndEc6XI-0hG0CWQ_oVQjaiPol6P2eMDLrSYpIrbiNMjHEDFwoOiKwG-ckgwPTHCiWypzK_reZohT7H5UdEYUmdlU_qq-oGQefv9q9A4mEkFV5WyiEFK5q5SsvsLR5QIKcjf1BhLDEfAIAAST有效时间: 7200

从测试结果中,可以看到,成功获取到了access_token,并且这个access_token的有效期是7200秒,也就是两个小时,和官方文档描述的一致。

一般在实际的项目开发中,我们都会把这个access_token缓存起来,缓存到本地或者nosql数据库中,然后每隔1.5个小时或2个小时的时候,就重新获取一次access_token,刷新缓存。这样做是为了避免在每个逻辑点都去重新获取access_token,因为这样会导致服务的不稳定,而且微信也规定了获取access_token的接口每天只能调用2000次,如果每个逻辑点都去重新获取access_token的话,不仅会导致服务不稳定,还容易把调用次数给花完。如下:

图片消息回复

本小节我们来看看如何进行图片消息的回复,官方文档地址如下:

https://mp./wiki?t=resource/res_main&id=mp1421140543

回复图片消息所需传递的参数如下:

官方的图文消息示例数据结构如下:

<xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[fromUser]]></FromUserName><CreateTime>12345678</CreateTime><MsgType><![CDATA[image]]></MsgType><Image><MediaId><![CDATA[media_id]]></MediaId></Image></xml>

从所需传递的参数列表中可以看到,回复图片消息时需要传递一个MediaId,这是通过素材管理中的接口上传多媒体文件,得到的id。所以在开发回复图片消息的接口前,我们还需要开发一个上传多媒体文件的接口,以此来获得MediaId。关于素材管理接口的官方文档地址如下:

https://mp./wiki?t=resource/res_main&id=mp1444738726

新增临时素材接口调用说明如下:

上传素材成功后,返回的参数如下:

有一点要说明的是,个人的订阅号是没有素材管理接口的权限的,所以我们需要将之前配置的appid和AppSecret配置为测试号的,不然接口会调用失败,如果是已认证的服务号就可以直接使用。

由于需要上传图片素材才能发送图片消息,所以首先需要在 WexinUtil 中,新增一个 upload 方法,用于上传临时图片素材并返回素材的media_id。

但是在写代码前,需要先将上传临时素材的接口url地址配置到SpringBoot的配置文件中,如下:

wechat:...uploadUrl: https://api./cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE

然后在配置类里加上这个配置的字段,如下:

...public class WeXinConfig {...private String uploadUrl;}

upload 方法代码如下:

/*** 上传临时素材** @param filePath 需要上传的文件所在路径* @param accessToken access_token* @param type 素材类型* @return media_id* @throws IOException*/public static String upload(String filePath, String accessToken, String type, String key) throws IOException {File file = new File(filePath);if (!(file.exists() || file.isFile())) {throw new IOException("文件不存在");}String url = wxConfig.getUploadUrl().replace("ACCESS_TOKEN", accessToken).replace("TYPE", type);URL urlObj = new URL(url);// 打开连接HttpURLConnection connection = (HttpURLConnection) urlObj.openConnection();// 设置属性connection.setRequestMethod("POST");connection.setDoInput(true);connection.setDoOutput(true);connection.setUseCaches(false);// 设置头信息connection.setRequestProperty("Connection", "Keep-Alive");connection.setRequestProperty("Charset", "UTF-8");// 设置边界String boundary = "----------" + System.currentTimeMillis();connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);StringBuilder sb = new StringBuilder();sb.append("--").append(boundary).append("\r\n").append("Content-Disposition;form-data;name=\"file\";filename=\"").append(file.getName()).append("\"\r\n").append("Content-Type:application/octet-stream\r\n\r\n");byte[] head = sb.toString().getBytes("UTF-8");// 获得输出流OutputStream output = new DataOutputStream(connection.getOutputStream());// 输出表头output.write(head);// 文件正文部分// 把文件以流文件的方式,推入到url中DataInputStream input = new DataInputStream(new FileInputStream(file));int bytes = 0;byte[] bufferOutput = new byte[1024];while ((bytes = input.read(bufferOutput)) != -1) {output.write(bufferOutput, 0, bytes);}input.close();// 结尾部分,定义最后数据分割线byte[] foot = ("\r\n--" + boundary + "--\r\n").getBytes("utf-8");output.write(foot);output.flush();output.close();StringBuilder buffer = new StringBuilder();String result = null;try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {String line = null;while ((line = reader.readLine()) != null) {buffer.append(line);}result = buffer.toString();} catch (IOException e) {e.printStackTrace();}JSONObject jsonObject = new JSONObject(result);log.info("response data: {}", jsonObject);return jsonObject.getString(key);}

在测试类中新增一个测试方法,测试代码如下:

@Testpublic void upload() throws IOException {String filePath = "Z:/v2-9b17df91629f842edd472d7cfcaa9c4b_hd.jpg";AccessToken accessToken = WeiXinUtil.getAccessToken();String mediaId = WeiXinUtil.upload(filePath, accessToken.getToken(), "image");System.out.println(mediaId);}

控制台输出结果如下:

mediaId: 5_PCrofX1_KIpSfWzJE-tu7AxQjxw6zlQ44oBuUkM_PZ6FiPeDY0a7vcWU2zdap9

获取到media_id后,就可以开始开发回复图片消息功能了,首先根据官方给出的数据结构,封装好各个实体类。Image类代码如下:

package org.zero01.weixin.mqdemo.vo;import com.thoughtworks.xstream.annotations.XStreamAlias;import lombok.Data;@Datapublic class Image {@XStreamAlias("MediaId")private String mediaId;}

ImageMessage类代码如下:

package org.zero01.weixin.mqdemo.vo;import com.thoughtworks.xstream.annotations.XStreamAlias;import lombok.Data;@Datapublic class ImageMessage extends BaseMassage {@XStreamAlias("Image")private Image image;}

然后在MessageUtil中新增如下两个方法:

/*** 将图片消息转换为xml** @param imageMessage* @return*/public static String imageMessageToXml(ImageMessage imageMessage) {XStream xStream = new XStream();xStream.processAnnotations(new Class[]{ImageMessage.class, Image.class});xStream.alias("xml", imageMessage.getClass());return xStream.toXML(imageMessage);}/*** 组装图片消息对象** @param toUserName* @param fromUserName* @return*/public static String initImageMessage(String toUserName, String fromUserName) {Image image = new Image();image.setMediaId("5_PCrofX1_KIpSfWzJE-tu7AxQjxw6zlQ44oBuUkM_PZ6FiPeDY0a7vcWU2zdap9");ImageMessage imageMessage = new ImageMessage();imageMessage.setFromUserName(toUserName);imageMessage.setToUserName(fromUserName);imageMessage.setMsgType(MessageTypeEnum.MSG_IMAGE.getMsgType());imageMessage.setCreateTime(System.currentTimeMillis());imageMessage.setImage(image);return imageMessageToXml(imageMessage);}

最后修改WeChatMqController中的text方法,增加一条判断,判断当用户输入数字2时,则回复图片消息。代码如下:

...if ("1".equals(allMessage.getContent())) {return MessageUtil.initNewsMessage(allMessage.getToUserName(), allMessage.getFromUserName());} else if ("2".equals(allMessage.getContent())) {return MessageUtil.initImageMessage(allMessage.getToUserName(), allMessage.getFromUserName());}...

完成以上代码的编写后,重启SpringBoot,打开微信公众号,测试结果如下:

音乐消息回复

在上一小节中,我们介绍了如何开发回复图片消息的功能,而其他类似的消息回复都是差不多的,这里就不一一去赘述了。本小节我们来看看如何进行音乐消息回复的开发,官方文档地址如下:

https://mp./wiki?t=resource/res_main&id=mp1421140543

回复音乐消息所需传递的参数如下:

官方的图文消息示例数据结构如下:

<xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[fromUser]]></FromUserName><CreateTime>12345678</CreateTime><MsgType><![CDATA[music]]></MsgType><Music><Title><![CDATA[TITLE]]></Title><Description><![CDATA[DESCRIPTION]]></Description><MusicUrl><![CDATA[MUSIC_Url]]></MusicUrl><HQMusicUrl><![CDATA[HQ_MUSIC_Url]]></HQMusicUrl><ThumbMediaId><![CDATA[media_id]]></ThumbMediaId></Music></xml>

开发音乐消息回复,我们需要一个音乐文件,找一个mp3文件放在工程的resources/static目录下即可,并确保能够在外网上访问:

根据官方给出的数据结构,封装好各个实体类。Music 实体类代码如下:

package org.zero01.weixin.mqdemo.vo;import com.thoughtworks.xstream.annotations.XStreamAlias;import lombok.Data;@Datapublic class Music {@XStreamAlias("Title")private String title;@XStreamAlias("Description")private String description;@XStreamAlias("MusicUrl")private String musicUrl;@XStreamAlias("HQMusicUrl")private String hQMusicUrl;@XStreamAlias("ThumbMediaId")private String thumbMediaId;}

MusicMessage 实体类代码如下:

package org.zero01.weixin.mqdemo.vo;import com.thoughtworks.xstream.annotations.XStreamAlias;import lombok.Data;@Datapublic class MusicMessage extends BaseMassage {@XStreamAlias("Music")private Music music;}

由于音乐消息需要传递一个ThumbMediaId,也就是缩略图的媒体id。所以我们需要修改之前的测试代码,以此获取thumb_media_id,如下:

@Testpublic void upload() throws IOException {String filePath = "Z:/v2-9b17df91629f842edd472d7cfcaa9c4b_hd.jpg";AccessToken accessToken = WeiXinUtil.getAccessToken();String thumbMediaId = WeiXinUtil.upload(filePath, accessToken.getToken(), "thumb","thumb_media_id");System.out.println("thumb_media_id: " + thumbMediaId);}

执行以上测试方法,控制台输出的结果如下:

thumb_media_id: Iu9puUGeFcS8HWyBGepJfeGoLDV_sWg8vJTeG-akMhcSGrqFjvoimMhCfjWw8F53

复制好thumb_media_id,然后在MessageUtil中新增如下两个方法:

/*** 将音乐消息转换为xml** @param musicMessage* @return*/public static String musicMessageToXml(MusicMessage musicMessage) {XStream xStream = new XStream();xStream.processAnnotations(new Class[]{MusicMessage.class, Music.class});xStream.alias("xml", musicMessage.getClass());return xStream.toXML(musicMessage);}/*** 组装音乐消息对象** @param toUserName* @param fromUserName* @return*/public static String initMusicMessage(String toUserName, String fromUserName) {Music music = new Music();music.setTitle("音乐消息");music.setDescription("这是一个音乐消息");music.setThumbMediaId("Iu9puUGeFcS8HWyBGepJfeGoLDV_sWg8vJTeG-akMhcSGrqFjvoimMhCfjWw8F53");music.setMusicUrl("/Unravel.mp3");music.setHQMusicUrl("/Unravel.mp3");MusicMessage musicMessage = new MusicMessage();musicMessage.setFromUserName(toUserName);musicMessage.setToUserName(fromUserName);musicMessage.setMsgType(MessageTypeEnum.MSG_MUSIC.getMsgType());musicMessage.setCreateTime(System.currentTimeMillis());musicMessage.setMusic(music);return musicMessageToXml(musicMessage);}

最后修改WeChatMqController中的text方法,增加一条判断,判断当用户输入数字3时,则回复音乐消息。代码如下:

...if ("1".equals(allMessage.getContent())) {return MessageUtil.initNewsMessage(allMessage.getToUserName(), allMessage.getFromUserName());} else if ("2".equals(allMessage.getContent())) {return MessageUtil.initImageMessage(allMessage.getToUserName(), allMessage.getFromUserName());} else if ("3".equals(allMessage.getContent())) {return MessageUtil.initMusicMessage(allMessage.getToUserName(), allMessage.getFromUserName());}...

完成以上代码的编写后,重启SpringBoot,打开微信测试公众号进行测试,测试结果如下:

点击音乐消息,打开后效果如下:

注:我这用的是pc端的微信,是可以正常播放的,但实际手机端很有可能无法播放,这也是微信的一个小坑

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。