一、前言
此文章仅代表个人的见解
二、微信接入
1、登录微信公众平台官网后,在公众平台后台管理页面 -开发者中心页,点击“修改配置”按钮,填写服务器地址(URL)、Token和EncodingAESKey,其中URL是开发者用来接收微信消息和事件的接口URL。Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)。EncodingAESKey由开发者手动填写或随机生成,将用作消息体加解密密钥。
同时,开发者可选择消息加解密方式:明文模式、兼容模式和安全模式。模式的选择与服务器配置在提交后都会立即生效,请开发者谨慎填写及选择。加解密方式的默认状态为明文模式,选择兼容模式和安全模式需要提前配置好相关加解密代码,详情请参考消息体签名及加解密部分的文档
2、开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带四个参数:signature(微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数),timestamp(时间戳),nonce(随机数),echostr(随机字符串)。
开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。
加密/校验流程如下:
1. 将token、timestamp、nonce三个参数进行字典序排序
2. 将三个参数字符串拼接成一个字符串进行sha1加密
3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
参考api地址http://mp.weixin.qq.com/wiki/17/2d4265491f12608cd170a95559800f2d.html
验证URL有效性成功后即接入生效,成为开发者。如果公众号类型为服务号(订阅号只能使用普通消息接口),可以在公众平台网站中申请认证,认证成功的服务号将获得众多接口权限,以满足开发者需求。
当你前面两部都搞定了的话,第三步就容易许多,基本根据api接口来就行了
三、进入代码开发
1、在上述的3步中,最为开始的疑问想必就是第二步了
当你在修改配置跟填写你自定义的token后,你所提交的地址必须就已经存在,并且能够接收微信推送过去的数据,同时进行验证,通过检验signature对请求进行校验,若校验成功则原样返回echostr,否则连提交保存都无法成功。
那么具体流程是这样子的,当你提交的时候,微信就发送了4个字段到你填写的url上
下面是我接收这四个字段的代码,由于微信二次开发在网上对struts的框架介绍比较少,故此处就介绍struts2跟微信对接,而不使用servlet,servlet虽然是个好东西,笔者比较少用,其实struts2也是基于servlet的,所以基本都看得懂。简便写上接收代码,假设下面代码是填写url访问后的处理逻辑
//打印接收的四个字段
System.out.println("signature:" + request.getParameter("signature"));
System.out.println("timestamp:" + request.getParameter("timestamp"));
System.out.println("nonce:" + request.getParameter("nonce"));
System.out.println("echostr:" + request.getParameter("echostr"));
// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
if (SignUtil.checkSignature(signature,timestamp,nonce)) {
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write(echostr);
}
return null;
SignUtil工具类如下,主要改了token,与公众平台上填写的要一致
package cn.com.aguang.weixin.utils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* 请求校验工具类
*
* @author aguang
* @Email [email protected]
*
*/
public class SignUtil {
// 与接口配置信息中的Token要一致
private static String token = "aguang";
/**
* 验证签名
*
* @param signature
* @param timestamp
* @param nonce
* @return
*/
public static boolean checkSignature(String signature, String timestamp,
String nonce) {
String[] arr = new String[] { token, timestamp, nonce };
// 将token、timestamp、nonce三个参数进行字典序排序
sort(arr);
StringBuilder content = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
content.append(arr[i]);
}
MessageDigest md = null;
String tmpStr = null;
try {
md = MessageDigest.getInstance("SHA-1");
// 将三个参数字符串拼接成一个字符串进行sha1加密
byte[] digest = md.digest(content.toString().getBytes());
tmpStr = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
content = null;
// 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
}
/**
* 将字节数组转换为十六进制字符串
*
* @param byteArray
* @return
*/
private static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
}
/**
* 将字节转换为十六进制字符串
*
* @param mByte
* @return
*/
private static String byteToHexStr(byte mByte) {
char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A',
'B', 'C', 'D', 'E', 'F' };
char[] tempArr = new char[2];
tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
tempArr[1] = Digit[mByte & 0X0F];
String s = new String(tempArr);
return s;
}
public static void sort(String a[]) {
for (int i = 0; i < a.length - 1; i++) {
for (int j = i + 1; j < a.length; j++) {
if (a[j].compareTo(a[i]) < 0) {
String temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
}
}
2、上述搭建后便可以进行提交了,如果保存成功,那么恭喜你,现在你可以进行第三步接口的开发了。
废话我就不多说了直接上代码,大家可以根据api文档,还有我以下附上的几个封装好的处理工具,以及对接的代码和测试类代码,基本根据参考这几个,也可以进行微信接口开发了,我推荐大家先自己摸索一下,如果有什么地方卡住了可以下面的代码,如果有什么不规范的地方或者写得不好的地方,可以留言说一下,下面的例子只针对几个接口做开发而已,具体后面的功能再加上。不对的地方请指出
3、笔者填写的地址处理类(框架s2sh,大家可以根据各自不同情况进行修改)
package cn.com.aguang.weixin.action.weixin;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.struts2.ServletActionContext;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import cn.com.aguang.weixin.bussiness.WeixinOpenIdBus;
import cn.com.aguang.weixin.bussiness.WeixinUtilBus;
import cn.com.aguang.weixin.dao.WeixinOpenIdDAO;
import cn.com.aguang.weixin.po.AgWeixinOpenId;
import cn.com.aguang.weixin.utils.HttpUtils;
import cn.com.aguang.weixin.utils.SignUtil;
import cn.com.aguang.weixin.utils.WeiXinContants;
import com.opensymphony.xwork2.ActionSupport;
@Controller
@Scope("prototype")
public class AguangWeiXinSoftAction extends ActionSupport {
// 微信opendId bus 类
private WeixinOpenIdBus weixinOpenIdBus;
// 为了维护行更高,觉得抽取业务逻辑的工具类,将业务一块一块分开
private WeixinUtilBus weixinUtilBus;
@Resource
public void setWeixinUtilBus(WeixinUtilBus weixinUtilBus) {
this.weixinUtilBus = weixinUtilBus;
}
@Resource
public void setWeixinOpenIdBus(WeixinOpenIdBus weixinOpenIdBus) {
this.weixinOpenIdBus = weixinOpenIdBus;
}
// 微信对接参数
private String signature;
// 微信对接参数
private String timestamp;
// 微信对接参数
private String nonce;
// 微信对接参数
private String echostr;
public String getSignature() {
return signature;
}
public void setSignature(String signature) {
this.signature = signature;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public String getNonce() {
return nonce;
}
public void setNonce(String nonce) {
this.nonce = nonce;
}
public String getEchostr() {
return echostr;
}
public void setEchostr(String echostr) {
this.echostr = echostr;
}
/**
*
*/
private static final long serialVersionUID = 3674789318078769142L;
/**
*
* 重大发现,经过api多个接口的测试研究,发现很多接口都通过这里来认证,并且对接
*
*
* 微信接入,token暗号对接,对应公众号微信平台管理开发填写的地址
*/
@Override
public String execute() throws Exception {
HttpServletResponse response = ServletActionContext.getResponse();
// JSONArray array = new JSONArray();
HttpServletRequest request = ServletActionContext.getRequest();
// 验证消息的真实性接收的参数,即填写url时点击保存提交的时候,能接收到下面4个参数
// System.out.println("signature:" + request.getParameter("signature"));
// System.out.println("timestamp:" + request.getParameter("timestamp"));
// System.out.println("nonce:" + request.getParameter("nonce"));
// System.out.println("echostr:" + request.getParameter("echostr"));
// 获取腾讯推送的数据流 经测试成功
InputStream is = request.getInputStream();
// 获取io流数据,如果是验证消息真实性即token,则不会有io流数据,否则一般为微信推送数据给我们,即有人关注了我们的公众号或者发送信息等推送事件
String pushData = this.weixinUtilBus.getInfoFromIoStream(is);
if (pushData != null && !"".equals(pushData)) {
Document document = DocumentHelper.parseText(pushData);
Element root = document.getRootElement();
String msgType = root.element("MsgType").getText();
if ("event".equals(msgType)) {
// 事件 click、unSubscribe、subscribe
String event = root.element("Event").getText();
if ("click".equals(event.toLowerCase())) {
// 点击按钮事件
String eventKey = root.element("EventKey").getText();
if ("aGuangSoftTest".equals(eventKey)) {
// 点击者openId
String fromUserName = root.element("FromUserName")
.getText();
// 接收的时间
int createTime = Integer.parseInt(root
.element("CreateTime").getText().toString());
// 回复内容
String content = WeiXinContants.AGUANG_SOFT_TEST_CLICK_CONTENT;
// 生成微信接收格式的XML字符串
String replyAguangSoftTestContentXMLStr = this.weixinUtilBus
.getReplyConentXmlStr(content, fromUserName,
createTime);
// 响应回去,注意是以text/xml的形式返回
response.setContentType("text/xml;charset=UTF-8");
response.getWriter().write(
replyAguangSoftTestContentXMLStr);
}
} else if ("unsubscribe".equals(event.toLowerCase())) {
// 取消关注事件
// 取消者openId
String fromUserName = root.element("FromUserName")
.getText();
// 将该记录查出,然后删除
List list = this.weixinOpenIdBus
.findByProperty(WeixinOpenIdDAO.OPEN_ID,
fromUserName);
if (list != null && list.size() > 0) {
this.weixinOpenIdBus.delete(list.get(0).getId());
}
} else if ("subscribe".equals(event.toLowerCase())) {
// 关注事件
// 关注者openId
String fromUserName = root.element("FromUserName")
.getText();
AgWeixinOpenId woi = new AgWeixinOpenId();
woi.setCreateTime(new Date());
woi.setOpenId(fromUserName);
this.weixinOpenIdBus.save(woi);
} else if ("scancode_waitmsg".equals(event)) {
String scanResult = root.element("ScanCodeInfo")
.element("ScanResult").getText();
int createTime = Integer.parseInt(root
.element("CreateTime").getText());
// 点击扫描者
String fromUserName = root.element("FromUserName")
.getText();
// 生成XML字符串数据
String scancodeResultXMLStr = this.weixinUtilBus
.scancodeResultXMLStr(scanResult, fromUserName,
createTime);
// 响应回去
response.setContentType("text/xml;charset=UTF-8");
response.getWriter().write(scancodeResultXMLStr);
}
} else if ("text".equals(msgType)) {
// 接收普通消息信息内容
// 发送消息者openId
String fromUserName = root.element("FromUserName").getText();
// 接收的时间
int createTime = Integer.parseInt(root.element("CreateTime")
.getText().toString());
// 接收的消息
String content = root.element("Content").getText();
Element weather = this.getWeatherInfoByCity(content);
if (weather == null) {
System.out.println("输入城市名可以查询当天的天气预报情况,如:广州");
// 生成微信接收格式的XML字符串
String replyAguangSoftTestContentXMLStr = this.weixinUtilBus
.getReplyConentXmlStr("输入城市名可以查询当天的天气预报情况,如:广州",
fromUserName, createTime);
// 响应回去,注意是以text/xml的形式返回
response.setContentType("text/xml;charset=UTF-8");
response.getWriter()
.write(replyAguangSoftTestContentXMLStr);
} else {
// 回复内容
String weatherInfo = "您输入的城市为:"
+ weather.element("city").getText() + ",天气:"
+ weather.element("status1").getText() + "->"
+ weather.element("status2").getText() + "。风向:"
+ weather.element("direction1").getText()
+ "。紫外线指数:"
+ weather.element("pollution").getText() + "。温度:"
+ weather.element("ssd_l").getText() + "。注意事项:"
+ weather.element("gm_s").getText() + "。"
+ weather.element("yd_s").getText() + "。"
+ weather.element("xcz_s").getText() + "。"
+ weather.element("pollution_s").getText();
System.out.println(weatherInfo);
// 生成微信接收格式的XML字符串
String replyAguangSoftTestContentXMLStr = this.weixinUtilBus
.getReplyConentXmlStr(weatherInfo, fromUserName,
createTime);
// 响应回去,注意是以text/xml的形式返回
response.setContentType("text/xml;charset=UTF-8");
response.getWriter()
.write(replyAguangSoftTestContentXMLStr);
}
}
}
is.close();
// 如果这3个参数有为空的,即不是验证消息的真实性,那么就返回空,因为是处理上面的其他时间,下面的验证无需执行
if (signature == null || timestamp == null || nonce == null) {
return null;
}
// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
if (SignUtil.checkSignature(signature, timestamp, nonce)) {
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write(echostr);
}
return null;
}
/**
* 获取最新accessToken
*
* @return String
*/
public String getAccessToken() {
String apiUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type="
+ WeiXinContants.GRANT_TYPE + "&appid=" + WeiXinContants.APP_ID
+ "&secret=" + WeiXinContants.APP_SECRET;
JSONObject json = JSONObject.fromObject(HttpUtils.getInstance()
.getJsonObjectByUrl(apiUrl));
String access_token = json.get("access_token") + "";
System.out.println("access_token:" + access_token);
return access_token;
}
/**
* 获取微信服务ip 列表
*/
public String getWeiXinServiceIp() {
String apiUrl = "https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token="
+ WeiXinContants.ACCESS_TOKEN;
JSONObject json = JSONObject.fromObject(HttpUtils.getInstance()
.getJsonObjectByUrl(apiUrl));
String ipStr = json.get("ip_list") + "";
System.out.println(ipStr);
return ipStr;
}
/**
* 创建菜单
*/
public String createMenu() throws Exception {
String apiUrl = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token="
+ new AguangWeiXinSoftAction().getAccessToken();
// 推送给微信服务器的json数据
JSONObject data = new JSONObject();
// 按钮json数组数据
JSONArray buttonJsonArray = new JSONArray();
// 菜单按钮1
JSONObject button1 = new JSONObject();
button1.put("type", "click");
button1.put("name", "普通按钮");
button1.put("key", "aGuangSoftTest");
// 添加按钮1
buttonJsonArray.add(button1);
// 菜单按钮2
JSONObject button2 = new JSONObject();
button2.put("name", "多级菜单");
JSONArray sub_button2 = new JSONArray();
// 菜单按钮2中的子按钮1
JSONObject button2Sub1 = new JSONObject();
// view表示点击后跳转链接
button2Sub1.put("type", "view");
button2Sub1.put("name", "谁的服务");
button2Sub1.put("url", "http://www.aguang.cc/weixin/");
// 菜单按钮2中的子按钮2
JSONObject button2Sub2 = new JSONObject();
// view表示点击后跳转链接
button2Sub2.put("type", "view");
button2Sub2.put("name", "我的信息");
button2Sub2.put("url", "http://www.aguang.cc/weixin/wx/");
// 菜单按钮2中的子按钮3
JSONObject button2Sub3 = new JSONObject();
// 表示点击后跳转链接
button2Sub3.put("type", "view");
button2Sub3.put("name", "赞一个");
button2Sub3.put("url", "http://www.aguang.cc/weixin/wx/");
sub_button2.add(button2Sub1);
sub_button2.add(button2Sub2);
sub_button2.add(button2Sub3);
// 将子菜单绑定到菜单2按钮中
button2.put("sub_button", sub_button2.toString());
// 添加菜单按钮2
buttonJsonArray.add(button2);
// 菜单按钮3
JSONObject button3 = new JSONObject();
button3.put("name", "最新按钮");
// 定义菜单按钮3的子按钮json数组
JSONArray sub_button3 = new JSONArray();
// 菜单按钮3中的子按钮1
JSONObject button3Sub1 = new JSONObject();
button3Sub1.put("type", "scancode_waitmsg");
button3Sub1.put("name", "扫码带提示");
button3Sub1.put("key", "searchCode");
button3Sub1.put("sub_button", "[]");
// 将子按钮绑定到菜单按钮3中
sub_button3.add(button3Sub1.toString());
// 菜单按钮3中的子按钮2
JSONObject button3Sub2 = new JSONObject();
button3Sub2.put("type", "scancode_push");
button3Sub2.put("name", "扫码推事件");
button3Sub2.put("key", "searchCodePushEvent");
button3Sub2.put("sub_button", "[]");
sub_button3.add(button3Sub2.toString());
button3.put("sub_button", sub_button3.toString());
buttonJsonArray.add(button3);
// 最终的按钮自定义菜单数据
data.put("button", buttonJsonArray.toString());
try {
System.out.println(data.toString());
HttpUtils.getInstance().createPostHttpRequest(apiUrl,
data.toString());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
*
* 微信菜单2的多级菜单1
*
* @return String
*/
public String menu2Button1() {
return SUCCESS;
}
/**
*
* 微信菜单2的多级菜单2
*
* @return String
*/
public String menu2Button2() {
return SUCCESS;
}
/**
*
* 微信菜单2的多级菜单1
*
* @return String
*/
public String menu2Button3() {
return SUCCESS;
}
/**
* 根据openId获取用户信息
*
* @return String
*/
public String getUserInfoByOpenId() {
String openId = "";
String apiUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token="
+ getAccessToken() + "&openid=" + openId + "&lang=zh_CN";
try {
HttpUtils.getInstance().createPostHttpRequest(apiUrl, "");
} catch (Exception e) {
e.printStackTrace();
}
return SUCCESS;
}
/**
*
* 根据城市名获取该城市的天气预报
*
* @return String
*/
private Element getWeatherInfoByCity(String city) {
try {
String apiUrl = "http://php.weather.sina.com.cn/xml.php?city="
+ URLEncoder.encode(city, "gbk")
+ "&password=DJOYnieT8234jlsK&day=0";
String data = HttpUtils.getInstance().getXmlDataByUrl(apiUrl);
System.out.println(data);
Document document = DocumentHelper.parseText(data);
Element root = document.getRootElement();
// 事件 click、unSubscribe、subscribe
Element weather = root.element("Weather");
if (weather == null) {
return null;
} else {
return weather;
}
} catch (Exception e) {
// e.printStackTrace();
System.out.println("转码失败");
return null;
}
}
}
4、action的测试类(自定义菜单主要是在测试类运行)
package cn.com.aguang.weixin.action.weixin;
import java.net.URLEncoder;
import java.util.Date;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.com.aguang.commons.business.BusinessException;
import cn.com.aguang.weixin.bussiness.WeixinOpenIdBus;
import cn.com.aguang.weixin.po.AgWeixinOpenId;
import cn.com.aguang.weixin.utils.HttpUtils;
import cn.com.aguang.weixin.utils.WeiXinContants;
import com.opensymphony.xwork2.interceptor.annotations.After;
import com.opensymphony.xwork2.interceptor.annotations.Before;
public class AguangWeiXinSoftTest {
private static WeixinOpenIdBus weixinOpenIdBus;
/**
* @throws java.lang.Exception
*/
@BeforeClass
public static void setUpBeforeClass() {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"applicationContext.xml");
weixinOpenIdBus = (WeixinOpenIdBus) ctx.getBean("weixinOpenIdBus");
}
/**
* @throws java.lang.Exception
*/
@Before
public void setUp() throws Exception {
System.out.println("---------------Begin");
}
/**
* @throws java.lang.Exception
*/
@After
public void tearDown() throws Exception {
System.out.println("---------------End");
}
/**
* 更新access_token
*/
public void updateAccessToken() {
String access_token = getAccessToken();
System.out.println("更新之前的access_token:" + WeiXinContants.ACCESS_TOKEN);
if (access_token != null && "".equals(access_token)) {
WeiXinContants.setAccessToken(access_token);
}
System.out.println("更新之后的access_token:" + access_token);
}
/**
* 获取微信服务ip 列表
*/
public void getWeiXinServiceIp() {
String apiUrl = "https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token="
+ WeiXinContants.ACCESS_TOKEN;
JSONObject json = JSONObject.fromObject(HttpUtils.getInstance()
.getJsonObjectByUrl(apiUrl));
String ipStr = json.get("ip_list") + "";
System.out.println(ipStr);
}
/**
* 创建自定义菜单
*/
public void createMenu() {
String apiUrl = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token="
+ getAccessToken();
// 推送给微信服务器的json数据
JSONObject data = new JSONObject();
// 按钮json数组数据
JSONArray buttonJsonArray = new JSONArray();
// 菜单按钮1
JSONObject button1 = new JSONObject();
button1.put("type", "click");
button1.put("name", "普通按钮");
button1.put("key", "aGuangSoftTest");
// 添加按钮1
buttonJsonArray.add(button1);
// 菜单按钮2
JSONObject button2 = new JSONObject();
button2.put("name", "多级菜单");
JSONArray sub_button2 = new JSONArray();
// 菜单按钮2中的子按钮1
JSONObject button2Sub1 = new JSONObject();
// view表示点击后跳转链接
button2Sub1.put("type", "view");
button2Sub1.put("name", "谁的服务");
button2Sub1.put("url",
"http://www.aguang.cc/weixin/wx/menu2Button1.action");
// 菜单按钮2中的子按钮2
JSONObject button2Sub2 = new JSONObject();
// view表示点击后跳转链接
button2Sub2.put("type", "view");
button2Sub2.put("name", "我的信息");
button2Sub2.put("url",
"http://www.aguang.cc/weixin/wx/menu2Button2.action");
// 菜单按钮2中的子按钮3
JSONObject button2Sub3 = new JSONObject();
// 表示点击后跳转链接
button2Sub3.put("type", "view");
button2Sub3.put("name", "赞一个");
button2Sub3.put("url",
"http://www.aguang.cc/weixin/wx/menu2Button3.action");
sub_button2.add(button2Sub1);
sub_button2.add(button2Sub2);
sub_button2.add(button2Sub3);
// 将子菜单绑定到菜单2按钮中
button2.put("sub_button", sub_button2.toString());
// 添加菜单按钮2
buttonJsonArray.add(button2);
// 菜单按钮3
JSONObject button3 = new JSONObject();
button3.put("name", "最新按钮");
// 定义菜单按钮3的子按钮json数组
JSONArray sub_button3 = new JSONArray();
// 菜单按钮3中的子按钮1
JSONObject button3Sub1 = new JSONObject();
button3Sub1.put("type", "scancode_waitmsg");
button3Sub1.put("name", "扫码带提示");
button3Sub1.put("key", "searchCode");
button3Sub1.put("sub_button", "[]");
// 将子按钮绑定到菜单按钮3中
sub_button3.add(button3Sub1.toString());
// 菜单按钮3中的子按钮2
JSONObject button3Sub2 = new JSONObject();
button3Sub2.put("type", "scancode_push");
button3Sub2.put("name", "扫码推事件");
button3Sub2.put("key", "searchCodePushEvent");
button3Sub2.put("sub_button", "[]");
sub_button3.add(button3Sub2.toString());
button3.put("sub_button", sub_button3.toString());
buttonJsonArray.add(button3);
// 最终的按钮自定义菜单数据
data.put("button", buttonJsonArray.toString());
try {
System.out.println(data.toString());
HttpUtils.getInstance().createPostHttpRequest(apiUrl,
data.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 根据openId获取用户信息,需要认证才可以,暂时无法使用,还没有认证
*
* @return String
*/
public void getUserInfoByOpenId() {
String openId = "cJCc8GeFCJu8kHocFLqg";
String apiUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token="
+ getAccessToken() + "&openid=" + openId + "&lang=zh_CN";
try {
JSONObject json = JSONObject.fromObject(HttpUtils.getInstance()
.createPostHttpRequest(apiUrl, ""));
System.out.println("国家" + json.get("country"));
System.out.println("省份" + json.get("province"));
System.out.println("城市" + json.get("city"));
System.out.println("昵称" + json.get("nickname"));
System.out.println("头像地址" + json.get("headimgurl"));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取最新accessToken
*
* @return String
*/
private String getAccessToken() {
String apiUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type="
+ WeiXinContants.GRANT_TYPE + "&appid=" + WeiXinContants.APP_ID
+ "&secret=" + WeiXinContants.APP_SECRET;
JSONObject json = JSONObject.fromObject(HttpUtils.getInstance()
.getJsonObjectByUrl(apiUrl));
String access_token = json.get("access_token") + "";
System.out.println("access_token:" + access_token);
return access_token;
}
/**
* 添加关注记录
*/
public void addWeixinOpenId() {
AgWeixinOpenId woi = new AgWeixinOpenId();
woi.setOpenId("123");
woi.setCreateTime(new Date());
try {
weixinOpenIdBus.save(woi);
} catch (BusinessException e) {
e.printStackTrace();
}
}
/**
*
* 根据城市获取天气预报信息
*
* @param city
*/
@Test
public void getWeatherInfoByCity() {
String city = "广州";
try {
String apiUrl = "http://php.weather.sina.com.cn/xml.php?city="
+ URLEncoder.encode(city, "gbk")
+ "&password=DJOYnieT8234jlsK&day=0";
String data = HttpUtils.getInstance().getXmlDataByUrl(apiUrl);
System.out.println(data);
Document document = DocumentHelper.parseText(data);
Element root = document.getRootElement();
// 事件 click、unSubscribe、subscribe
Element weather = root.element("Weather");
if (weather == null) {
System.out.println("无数据");
} else {
// 回复内容
String weatherInfo = "您输入的城市为:"
+ weather.element("city").getText() + "。天气:"
+ weather.element("status1").getText() + "->"
+ weather.element("status2").getText() + "。风向:"
+ weather.element("direction1").getText() + "。紫外线指数:"
+ weather.element("pollution").getText() + "。温度:"
+ weather.element("ssd_l").getText() + "。注意事项:"
+ weather.element("gm_s").getText() + "。"
+ weather.element("yd_s").getText() + "。"
+ weather.element("xcz_s").getText() + "。"
+ weather.element("pollution_s").getText();
System.out.println(weatherInfo);
}
} catch (Exception e) {
// e.printStackTrace();
System.out.println("转码失败");
}
}
}
package cn.com.aguang.weixin.bussiness.impl;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import cn.com.aguang.commons.business.AbstractBaseBus;
import cn.com.aguang.commons.dao.BaseDAO;
import cn.com.aguang.weixin.bussiness.WeixinOpenIdBus;
import cn.com.aguang.weixin.dao.WeixinOpenIdDAO;
import cn.com.aguang.weixin.po.AgWeixinOpenId;
@Service("weixinOpenIdBus")
public class WeixinOpenIdBusImpl extends
AbstractBaseBus implements WeixinOpenIdBus {
private WeixinOpenIdDAO weixinOpenIdDAO;
@Resource
public void setWeixinOpenIdDAO(WeixinOpenIdDAO weixinOpenIdDAO) {
this.weixinOpenIdDAO = weixinOpenIdDAO;
}
@Override
protected BaseDAO getDAO() {
return weixinOpenIdDAO;
}
@Override
protected void checkRequires(AgWeixinOpenId paramT) {
// do nothing
}
}
package cn.com.aguang.weixin.bussiness.impl;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.stereotype.Service;
import cn.com.aguang.weixin.bussiness.WeixinUtilBus;
import cn.com.aguang.weixin.utils.WeiXinContants;
@Service("weixinUtilBus")
public class WeixinUtilBusImpl implements WeixinUtilBus {
/**
* 根据IO流获取IO流的数据
*/
@Override
public String getInfoFromIoStream(InputStream is) {
String pushData = "";
try {
if (is != null) {
String charSet = null;
charSet = "utf-8";
byte b[] = new byte[200];
int numRead = is.read(b);
if (numRead != -1) {
String content = new String(b, 0, numRead);
while (numRead != -1) {
numRead = is.read(b);
if (numRead != -1) {
String newContent = new String(b, 0, numRead,
charSet);
content += newContent;
}
}
pushData = content;
System.out.println("接收到推送的信息:" + content);
}
}
return pushData;
} catch (IOException e) {
return "";
}
}
/**
* 根据内容,接收者openId获取应该相应的XML字符串
*/
@Override
public String getReplyConentXmlStr(String content, String toUserName,
int createTime) {
String replyAguangSoftTestContentXMLStr = ""
+ createTime
+ " ";
return replyAguangSoftTestContentXMLStr;
}
/**
* 根据扫描内容,接收者openId获取应该相应的XML字符串
*/
@Override
public String scancodeResultXMLStr(String scanResult, String toUserName,
int createTime) {
String scancodeResultXMLStr = ""
+ createTime
+ " ";
return scancodeResultXMLStr;
}
}
package cn.com.aguang.weixin.utils;
public class WeiXinContants {
public static final String WEI_XIN_NO = "AGuangSoftTest"; //公众平台设置的微信号,发送信息时需要
public static final String APP_ID = "**************";//根据自己的替换
public static final String APP_SECRET = "*************************"; 根据自己的替换
public static final String TOKEN = "***"; //根据自己的替换
public static final String GRANT_TYPE = "client_credential";
public static String ACCESS_TOKEN = "Cx_JPR-Cs791HRhDcA2jjm9U_UtHEPaOXT3q-LL7six96i9u0_-olxXoaHUbhGz3Cal7sp2C2KM0qerk4fpPnU7_iZCKhi42nOaKE2SA-Os";
public static String AGUANG_SOFT_TEST_CLICK_CONTENT = "接口点击事件回复调试成功,这只是一小步";
public static void setAccessToken(String access_token) {
ACCESS_TOKEN = access_token;
}
}
封装的请求工具类
package cn.com.aguang.weixin.utils;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
/**
*
* 饿汉试 单例 直接实例化一个,线程安全(以前一直用懒汉式,第一次调用实例化,但是有可能会出现多个实例,非常小的概率)
*
* @author aGuang
*
*/
public class HttpUtils {
private static HttpUtils instance = new HttpUtils();
/**
* 私有构造方法
*/
private HttpUtils() {
}
public static HttpUtils getInstance() {
return instance;
}
/**
* 根据api访问地址获取地址返回的json数据
*
* @param apiUrl
*
* @return json
*/
public String getJsonObjectByUrl(String apiUrl) {
try {
URL url = new URL(apiUrl);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setAllowUserInteraction(false);
InputStream urlStream = url.openStream();
String charSet = null;
charSet = "utf-8";
byte b[] = new byte[200];
int numRead = urlStream.read(b);
String content = new String(b, 0, numRead);
while (numRead != -1) {
numRead = urlStream.read(b);
if (numRead != -1) {
String newContent = new String(b, 0, numRead, charSet);
content += newContent;
}
}
// System.out.println("content:" + content);
JSONObject json = JSONObject.fromObject(new String(content
.getBytes(), "utf-8"));
urlStream.close();
return json.toString();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据api访问地址获取地址返回的json数据
*
* @param apiUrl
*
* @return
*/
public String getJsonArrayByUrl(String apiUrl) {
try {
URL url = new URL(apiUrl);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setAllowUserInteraction(false);
InputStream urlStream = url.openStream();
String charSet = null;
charSet = "utf-8";
byte b[] = new byte[200];
int numRead = urlStream.read(b);
String content = new String(b, 0, numRead);
while (numRead != -1) {
numRead = urlStream.read(b);
if (numRead != -1) {
String newContent = new String(b, 0, numRead, charSet);
content += newContent;
}
}
// System.out.println("content:" + content);
JSONArray array = JSONArray.fromObject(new String(content
.getBytes(), "utf-8"));
urlStream.close();
return array.toString();
} catch (Exception e) {
// e.printStackTrace();
return null;
}
}
/**
* 根据api访问地址获取地址返回的XML数据
*
* @param apiUrl
*
* @return
*/
public String getXmlDataByUrl(String apiUrl) {
try {
URL url = new URL(apiUrl);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setAllowUserInteraction(false);
InputStream urlStream = url.openStream();
String charSet = null;
charSet = "utf-8";
byte b[] = new byte[6000];
int numRead = urlStream.read(b);
String content = new String(b, 0, numRead);
while (numRead != -1) {
numRead = urlStream.read(b);
if (numRead != -1) {
String newContent = new String(b, 0, numRead, charSet);
content += newContent;
}
}
// System.out.println("content:" + content);
String xmlString = new String(content.getBytes(), "utf-8");
urlStream.close();
return xmlString;
} catch (Exception e) {
// e.printStackTrace();
return null;
}
}
/**
* 用post请求访问一个http
*
* @param apiUrl
* 请求地址
* @return
*/
public String createPostHttpRequest(String apiUrl, String data)
throws Exception {
// Post请求的url,与get不同的是不需要带参数
URL postUrl = new URL(apiUrl);
// 打开连接
HttpURLConnection connection = (HttpURLConnection) postUrl
.openConnection();
// 25秒连接超时
connection.setConnectTimeout(25000);
// 读取超时 --服务器响应比较慢,增大时间
connection.setReadTimeout(25000);
HttpURLConnection.setFollowRedirects(true);
// 设置是否向connection输出,因为这个是post请求,参数要放在
// http正文内,因此需要设为true
connection.setDoOutput(true);
// Read from the connection. Default is true.
connection.setDoInput(true);
// Set the post method. Default is GET
connection.setRequestMethod("POST");
// Post cannot use caches
// Post 请求不能使用缓存
connection.setUseCaches(false);
// URLConnection.setFollowRedirects是static函数,作用于所有的URLConnection对象。
// connection.setFollowRedirects(true);
// URLConnection.setInstanceFollowRedirects是成员函数,仅作用于当前函数
connection.setInstanceFollowRedirects(true);
// 配置本次连接的Content-type,配置为application/x-www-form-urlencoded的
// 意思是正文是urlencoded编码过的form参数,下面我们可以看到我们对正文内容使用URLEncoder.encode
// 进行编码
// connection.setRequestProperty("Content-Type",
// "application/x-www-form-urlencoded");
connection
.setRequestProperty("User-Agent",
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0");
connection.setRequestProperty("Referer", "https://api.weixin.qq.com/");
// 连接,从postUrl.openConnection()至此的配置必须要在connect之前完成,
// 要注意的是connection.getOutputStream会隐含的进行connect。
connection.connect();
OutputStreamWriter out = new OutputStreamWriter(
connection.getOutputStream());
// The URL-encoded contend
// 正文,正文内容其实跟get的URL中'?'后的参数字符串一致
// String content = URLEncoder.encode("中国聚龙", "utf-8");
String content = data;
// DataOutputStream.writeBytes将字符串中的16位的unicode字符以8位的字符形式写道流里面
out.write(content);
out.flush();
out.close(); // flush and close
BufferedReader reader = new BufferedReader(new InputStreamReader(
connection.getInputStream(), "utf-8"));// 设置编码,否则中文乱码
String line = "";
System.out.println("=============================");
System.out.println("Contents of post request");
System.out.println("=============================");
while ((line = reader.readLine()) != null) {
// line = new String(line.getBytes(), "utf-8");
System.out.println("返回的信息:" + line);
}
System.out.println("=============================");
System.out.println("Contents of post request ends");
System.out.println("=============================");
reader.close();
connection.disconnect();
return "";
}
}
有什么好的建议也可以给我,对于更多要了解微信的,我建议还是多看微信提供的api文档,本文只供参考,希望对大家有所帮助(其实主要是代码,我个人觉得新手来说,代码就是王道,但是后面还有多数的接口使用,大家可以自己摸索使用)