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

本篇文章講的不是很深的內容,如有不足歡迎指出~~~

歡迎提問,轉載~~~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM