在完成開發接入的第一步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
本篇文章講的不是很深的內容,如有不足歡迎指出~~~
歡迎提問,轉載~~~