Java开发微信公号-----第二步:获取access_token、消息回复、创建菜单
在完成开发接入的第一步http://www.cnblogs.com/longLifeFrog/articles/8968903.html之后
现在开始弄一些简单的功能来调用微信那边的接口。
一些和http、https、xml有关的工具类在
HttpUtil:http://www.cnblogs.com/longLifeFrog/articles/8974098.html
XmlUtil:http://www.cnblogs.com/longLifeFrog/articles/8975232.html
或者登陆github看代码,具体的链接在这:https://github.com/617355557/code_pianduan
这两篇博客中有介绍。
获取access_token:
先看微信那边的文档(https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183)l链接过去会重定向,我就下面放图画出重点,

由上面可知access_token应该是统一的一个,在2h内由AppID和AppSecret确定,保存长度要至少512个字符空间,对于刷新不能各自刷新,由中控服务器刷新,在获取token之前要将服务器ip加入白名单。
由于测试账号不用设置白名单,我是没设置过,正式的账号白名单是这样找的:
登陆成功后的个人首页,拉到最下面有个基本配置

然后这里可以配置:

请求参数和返回参数如下图所示:

所以代码具体如下:
@RequestMapping(value = "getAccessToken", method = { RequestMethod.GET })
public void getAccessToken(HttpServletRequest request, HttpServletResponse response) {
try {
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
url = url.replace("APPID", appId).replace("APPSECRET", appsecret);
String result = HttpUtil.doGetSSL(url, "utf-8");
JSONObject json = JSONObject.parseObject(result);
request.getSession().setAttribute("accessToken", json.get("access_token"));
PrintWriter pw = response.getWriter();
pw.println(result);
pw.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
创建菜单:
官方文档https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141013


我用的是官方的click和view菜单例子,不过要做个修改:
click和view的请求示例
{ "button":[ { "type":"click", "name":"今日歌曲", "key":"V1001_TODAY_MUSIC" }, { "name":"菜单", "sub_button":[ { "type":"view", "name":"搜索", "url":"http://www.soso.com/" }, { "type":"miniprogram", "name":"wxa", "url":"http://mp.weixin.qq.com", "appid":"wx286b93c14bbf93aa", "pagepath":"pages/lunar/index" }, { "type":"click", "name":"赞一下我们", "key":"V1001_GOOD" }] }] }
这里如果缘分不动拷进代码,作为post请求的参数,那么绝对会得到错误过关于appid的,
原因是下面代码是小程序类型的菜单,需要对应的小程序appid,由于我们的代码没有准备对应的小程序,所以要将上面代码中的这段删掉。
{
"type":"miniprogram",
"name":"wxa",
"url":"http://mp.weixin.qq.com",
"appid":"wx286b93c14bbf93aa",
"pagepath":"pages/lunar/index"
}
controller代码:
@RequestMapping(value = "cgiMenu", method = { RequestMethod.GET })
public void cgiMenu(HttpServletRequest request, HttpServletResponse response) {
try {
String url = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
String accessToken = String.valueOf(request.getSession().getAttribute("accessToken"));
System.out.println(accessToken);
url = url.replace("ACCESS_TOKEN", accessToken);
String jsonstr = "{\"button\":[{\"type\":\"click\",\"name\":\"今日歌曲\",\"key\":\"V1001_TODAY_MUSIC\"},{\"name\":\"菜单\",\"sub_button\":[{\"type\":\"view\",\"name\":\"搜索\",\"url\":\"http://www.soso.com/\"},{\"type\":\"click\",\"name\":\"赞一下我们\",\"key\":\"V1001_GOOD\"}]}]}";
String result = HttpUtil.doPostSSL(url, jsonstr);
JSONObject json = JSONObject.parseObject(result);
request.getSession().setAttribute("accessToken", json.get("access_token"));
PrintWriter pw = response.getWriter();
response.getWriter().println(result);
response.getWriter().flush();
} catch (Exception e) {
e.printStackTrace();
}
}
消息回复(消息排重没弄,有兴趣的可以自己试试,本篇文章只是简单的入门):
在第一步(http://www.cnblogs.com/longLifeFrog/articles/8968903.html)中不是有个test接口来进行开发对接的吗?消息回复就是用的那个接口。
我们来看下文档(https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140453):

所以那个接口需要能接受post请求,要在@RequestMapping里面的method值加上RequestMethod.POST
然后判断请求是get还是post,如果是get就是进行对接验证,如果是post就是消息回复。
@RequestMapping(value = "test", method = { RequestMethod.GET, RequestMethod.POST })
public void validWXURl(HttpServletRequest request, HttpServletResponse response) {
PrintWriter out = null;
try {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
out = response.getWriter();
Boolean isGet = request.getMethod().toLowerCase().equals("get");
if (isGet) {// get请求
//开发接入调试
InterfaceMethod.testWXUrlIsValid(request, response);
} else {
String respMessage = "异常消息";
respMessage=InterfaceMethod.replyMessage(request,respMessage);
System.out.println(respMessage);
out.write(respMessage);
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
需要新建消息的实体类,如果看过文档的同学可以知道一个结论
private String toUserName; private String fromUserName; private String createTime; private String msgType; private String msgId;
这几个属性在那些消息对象里面都存在,所以我们需要弄个基础消息类BaseMessage作为父类,代码中的注解先不用理他,后面自然就知道了。
public class BaseMessage {
@XStreamAlias("ToUserName")
@CDATA_Annotaion
private String toUserName;
@XStreamAlias("FromUserName")
@CDATA_Annotaion
private String fromUserName;
@XStreamAlias("CreateTime")
private String createTime;
@XStreamAlias( "MsgType")
@CDATA_Annotaion
private String msgType;
@XStreamAlias("MsgId")
private String msgId;
public String getToUserName() {
return toUserName;
}
public void setToUserName(String toUserName) {
this.toUserName = toUserName;
}
public String getFromUserName() {
return fromUserName;
}
public void setFromUserName(String fromUserName) {
this.fromUserName = fromUserName;
}
public String getCreateTime() {
return createTime;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
public String getMsgType() {
return msgType;
}
public void setMsgType(String msgType) {
this.msgType = msgType;
}
public String getMsgId() {
return msgId;
}
public void setMsgId(String msgId) {
this.msgId = msgId;
}
}
还需要一个具体的类作为子类继承BaseMessage父类,
比如TextMessage文本消息:
public class TextMessage extends BaseMessage {
@XStreamAlias("Content")
@CDATA_Annotaion
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
准备好了实体类,那么剩下的就是调用接口了,
一文本消息为例,文本消息的数据包格式如下:

那么我们需要一个解析xml字符串并且转换成map的工具类,详情请看XmlUtil:http://www.cnblogs.com/longLifeFrog/articles/8975232.html,
github地址:https://github.com/617355557/code_pianduan/blob/master/XmlUtil.java
都有做具体的介绍,不懂得欢迎提问。
关于XmlUtil中的textMessage2Xml方法,最开始可以这样子写(不过简单写有要注意的地方看本文章结尾的注意事项),如果要加入CDATA标签就用我github上面那种,

业务逻辑代码:
public static String replyMessage(HttpServletRequest request,String respMessage) {
try {
Map<String,String> reqMap=XmlUtil.xml2Map(request);
//发送方账号(open_id)
String fromUserName = reqMap.get("FromUserName");
//公众账号
String toUserName = reqMap.get("ToUserName");
//消息类型
String msgType = reqMap.get("MsgType").toLowerCase();
//消息内容
String content = reqMap.get("Content");
log.info("FromUserName is:" + fromUserName + ", ToUserName is:" + toUserName + ", MsgType is:" + msgType);
TextMessage text=new TextMessage();
text.setToUserName(fromUserName);
text.setFromUserName(toUserName);
text.setCreateTime(new Date().getTime()+"");
//文本消息回复
if(msgType.equals(MESSAGE_TYPE.REQ_TYPE_TEXT.getValue())) {
if("苟利国家生死以".equals(content)) {
content="岂因祸福避趋之";
}
text.setContent(content);
text.setMsgType(msgType);
respMessage=XmlUtil.textMessage2Xml(text,TextMessage.class);
}
//订阅事件回复
if(msgType.equals(MESSAGE_TYPE.REQ_TYPE_EVENT.getValue())) {
String eventType=reqMap.get("Event").toLowerCase();
if(eventType.equals(EVENT_TYPE.SUBSCRIBE.getValue())) {
text.setContent("欢迎关注");
text.setMsgType(MESSAGE_TYPE.RESP_TYPE_TEXT.getValue());
respMessage=XmlUtil.textMessage2Xml(text,TextMessage.class);
}else if(eventType.equals(EVENT_TYPE.UNSUBSCRIBE.getValue())) {
}else if(eventType.equals(EVENT_TYPE.CLICK.getValue())) {
//点击点赞菜单的事件回复消息
String eventKey=reqMap.get("EventKey");
//V1001_GOOD是之前创建自定义菜单时候的一个菜单的key
if("V1001_GOOD".equals(eventKey)) {
//被赞次数+1,记录点赞人
text.setContent("谢谢您的支持");
text.setMsgType(MESSAGE_TYPE.RESP_TYPE_TEXT.getValue());
respMessage=XmlUtil.textMessage2Xml(text,TextMessage.class);
}
}
}
} catch (IOException e) {
log.error("服务器异常",e);
e.printStackTrace();
}
return respMessage;
}
代码中有用到枚举类MESSAGE_TYPE和EVENT_TYPE
MESSAGE_TYPE(返回参数MsgType和这个有关):
public enum MESSAGE_TYPE {
RESP_TYPE_TEXT("text"),
RESP_TYPE_MUSIC("music"),
RESP_TYPE_NEWS("news"),
REQ_TYPE_TEXT("text"),
REQ_TYPE_IMAGE("image"),
REQ_TYPE_LINK("link"),
REQ_TYPE_LOCATION("location"),
REQ_TYPE_VOICE("voice"),
REQ_TYPE_EVENT("event");
private String value;
private MESSAGE_TYPE(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
EVENT_TYPE(和返回参数EVENT有关,属于接收事件推送部分https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140454):
public enum EVENT_TYPE {
SUBSCRIBE("subscribe"),
UNSUBSCRIBE("unsubscribe"),
CLICK("click");
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
private EVENT_TYPE(String value) {
this.value = value;
}
}
注意事项:要注意返回的xml字符串里面标签名大小写问题,msgType微信解析不了,一定会报错,一定要是MsgType!这个非常重要!!!
最后开启服务和ngrok进行测试吧~~~
附上项目github地址:https://github.com/617355557/ssm-wx-gzh2
本篇文章讲的不是很深的内容,如有不足欢迎指出~~~
欢迎提问,转载~~~
浙公网安备 33010602011771号