开发环境的准备
转载地址:http://www.2cto.com/weixin/201505/401807.html
- 微信公众号
- 外网映射工具(开发调试)
与微信的对接的URL应该满足以下的条件:
- 在公网上能够访问
- 只支持80端口
映射工具有很多,例如花生壳,ngrok可以将内网映射到公网上面,这样就可以使用公网访问本机的网络服务。下载链接: http://pan.baidu.com/s/1i3u26St 密码: v4e8(里面有简明的教程)。
微信公众号的数据交互原理
我们的主要开发就是微信公众号服务器的开发。
开发模式的接入
进入微信公众号平台之后进入开发者中心,在开发者中心中找到开发者文档,在新手指南中有接入的相关步骤。依据接入文档有以下的实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
package
org.gpf.servlet;
import
java.io.IOException;
import
java.io.PrintWriter;
import
javax.servlet.ServletException;
import
javax.servlet.http.HttpServlet;
import
javax.servlet.http.HttpServletRequest;
import
javax.servlet.http.HttpServletResponse;
import
org.gpf.util.CheckUtil;
/**
* 接收微信服务器发送的4个参数并返回echostr
*/
public
class
WeixinServlet
extends
HttpServlet {
public
void
doGet(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
// 接收微信服务器以Get请求发送的4个参数
String signature = request.getParameter(
"signature"
);
String timestamp = request.getParameter(
"timestamp"
);
String nonce = request.getParameter(
"nonce"
);
String echostr = request.getParameter(
"echostr"
);
PrintWriter out = response.getWriter();
if
(CheckUtil.checkSignature(signature, timestamp, nonce)) {
out.print(echostr);
// 校验通过,原样返回echostr参数内容
}
}
public
void
doPost(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
doGet(request, response);
}
}
|
校验工具类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package
org.gpf.util;
import
java.util.Arrays;
import
org.apache.commons.codec.digest.DigestUtils;
/**
* 校验的工具类
*/
public
class
CheckUtil {
private
static
final
String token =
"weixin"
;
public
static
boolean
checkSignature(String signature,String timestamp,String nonce){
String[] arr =
new
String[] { token, timestamp, nonce };
// 排序
Arrays.sort(arr);
// 生成字符串
StringBuilder content =
new
StringBuilder();
for
(
int
i =
0
; i < arr.length; i++) {
content.append(arr[i]);
}
// sha1加密
String temp = getSHA1String(content.toString());
return
temp.equals(signature);
// 与微信传递过来的签名进行比较
}
private
static
String getSHA1String(String data){
return
DigestUtils.sha1Hex(data);
// 使用commons codec生成sha1字符串
}
}
|
Servlet配置:
1
2
3
4
5
6
7
8
9
|
servlet>
<servlet-name>WeixinServlet</servlet-name>
<servlet-
class
>org.gpf.servlet.WeixinServlet</servlet-
class
>
</servlet>
<servlet-mapping>
<servlet-name>WeixinServlet</servlet-name>
<url-pattern>/wx.
do
</url-pattern>
</servlet-mapping>
|
接下来通过映射工具将本地的服务器映射到公网,从公网访问Servlet。
开发模式和编辑模式是互斥的,如果启动了开发模式,则自定义菜单和自动回复将失效!
消息的接收和响应
参照文档,当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。所以我们需要更改我们的Servlet中的doPost方法,因为微信服务器与我们的服务器之间是通过XML传递数据的,因此我们需要实现消息实体与XML之间的互相转换。可以采用第三方jar包XStream完成。
处理微信服务器与本机服务器进行交互的Servlet:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
package
org.gpf.servlet;
import
java.io.IOException;
import
java.io.PrintWriter;
import
java.util.Date;
import
java.util.Map;
import
javax.servlet.ServletException;
import
javax.servlet.http.HttpServlet;
import
javax.servlet.http.HttpServletRequest;
import
javax.servlet.http.HttpServletResponse;
import
org.dom4j.DocumentException;
import
org.gpf.po.TextMeaasge;
import
org.gpf.util.CheckUtil;
import
org.gpf.util.MessageUtil;
/**
* 微信消息的接收和响应
*/
public
class
WeixinServlet
extends
HttpServlet {
/**
* 接收微信服务器发送的4个参数并返回echostr
*/
public
void
doGet(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
// 接收微信服务器以Get请求发送的4个参数
String signature = request.getParameter(
"signature"
);
String timestamp = request.getParameter(
"timestamp"
);
String nonce = request.getParameter(
"nonce"
);
String echostr = request.getParameter(
"echostr"
);
PrintWriter out = response.getWriter();
if
(CheckUtil.checkSignature(signature, timestamp, nonce)) {
out.print(echostr);
// 校验通过,原样返回echostr参数内容
}
}
/**
* 接收并处理微信客户端发送的请求
*/
public
void
doPost(HttpServletRequest request, HttpServletResponse response)
throws
ServletException, IOException {
request.setCharacterEncoding(
"utf-8"
);
response.setContentType(
"text/xml;charset=utf-8"
);
PrintWriter out = response.getWriter();
try
{
Map<String, String> map = MessageUtil.xmlToMap(request);
String toUserName = map.get(
"ToUserName"
);
String fromUserName = map.get(
"FromUserName"
);
String msgType = map.get(
"MsgType"
);
String content = map.get(
"Content"
);
String message =
null
;
if
(
"text"
.equals(msgType)) {
// 对文本消息进行处理
TextMeaasge text =
new
TextMeaasge();
text.setFromUserName(toUserName);
// 发送和回复是反向的
text.setToUserName(fromUserName);
text.setMsgType(
"text"
);
text.setCreateTime(
new
Date().getTime());
text.setContent(
"你发送的消息是:"
+ content);
message = MessageUtil.textMessageToXML(text);
System.out.println(message);
}
out.print(message);
// 将回应发送给微信服务器
}
catch
(DocumentException e) {
e.printStackTrace();
}
finally
{
out.close();
}
}
}
|
按照微信的接口文档编写的文本消息实体类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
package
org.gpf.util;
import
java.io.IOException;
import
java.io.InputStream;
import
java.util.HashMap;
import
java.util.List;
import
java.util.Map;
import
javax.servlet.http.HttpServletRequest;
import
org.dom4j.Document;
import
org.dom4j.DocumentException;
import
org.dom4j.Element;
import
org.dom4j.io.SAXReader;
import
org.gpf.po.TextMeaasge;
import
com.thoughtworks.xstream.XStream;
/**
* 实现消息的格式转换(Map类型和XML的互转)
*/
public
class
MessageUtil {
/**
* 将XML转换成Map集合
*/
public
static
Map<String, String>xmlToMap(HttpServletRequest request)
throws
IOException, DocumentException{
Map<String, String> map =
new
HashMap<String, String>();
SAXReader reader =
new
SAXReader();
// 使用dom4j解析xml
InputStream ins = request.getInputStream();
// 从request中获取输入流
Document doc = reader.read(ins);
Element root = doc.getRootElement();
// 获取根元素
List<Element> list = root.elements();
// 获取所有节点
for
(Element e : list) {
map.put(e.getName(), e.getText());
System.out.println(e.getName() +
"--->"
+ e.getText());
}
ins.close();
return
map;
}
/**
* 将文本消息对象转换成XML
*/
public
static
String textMessageToXML(TextMeaasge textMessage){
XStream xstream =
new
XStream();
// 使用XStream将实体类的实例转换成xml格式
xstream.alias(
"xml"
, textMessage.getClass());
// 将xml的默认根节点替换成“xml”
return
xstream.toXML(textMessage);
}
}
|