1.实现步骤
(1)概述
其实将公众号接入图灵机器人,就是相当于微信将用户发的信息传到自己实现的服务器,服务器再将信息传给图灵机器人,获取图灵返回结果响应给用户。
(2)步骤
①解析接收的微信信息,提取信息
②调用图灵接口发送请求
③解析图灵响应,提取信息
④封装微信响应,回复用户
2.程序实现
项目使用了Springboot框架构建
(1)解析接收的微信信息
①查看微信公众平台api文档接收普通消息的格式
此处只处理文本消息,看到微信推送过来的xml格式的:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[this is a test]]></Content>
<MsgId>1234567890123456</MsgId>
</xml>
我们需要关注的是Content里面的内容
②读取请求输入流
微信服务器将POST消息的XML数据包到开发者填写的URL上,所以下面接收的是POST形式的请求
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
//解析请求
InputStream inputStream = req.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
String str;
StringBuilder builder = new StringBuilder();
while (null != (str = br.readLine())) {
builder.append(str);
}
str = builder.toString();
}
③定义一个pojo类,和xml里的字段对应上,注意,此处为了反射注入方便,没遵循驼峰命名。
/**
* weChat接收普通消息的实体类
*/
public class ReceiveEntity {
private String ToUserName;//开发者微信号
private String FromUserName;//发送方帐号
private String CreateTime;//消息创建时间
private String MsgType;//消息类型
private String Content;//文本消息内容
private String MsgId;//消息id
//....getter、setter
}
④通过dom4j解析注入ReceiveEntity 实例
public ReceiveEntity parseWeChatMsgAndReturnEntity(String content) {
ReceiveEntity res = null;
try {
Document doc = DocumentHelper.parseText(content);
Element root = doc.getRootElement();
Iterator<Element> iter = root.elementIterator();
//反射注入属性
Class clazz = Class.forName("包路径.ReceiveEntity");
res = (ReceiveEntity) clazz.newInstance();
while (iter.hasNext()) {
Element element = iter.next();
Field field = clazz.getDeclaredField(element.getName());
Method method = clazz.getDeclaredMethod("set" + element.getName(), field.getType());
method.invoke(res, element.getText());
}
} catch (Exception e) {
e.printStackTrace();
}
return res;
}
(2)调用图灵接口发送请求
①查看图灵API V2.0接入文档
知道请求方式是HTTP POST,请求参数格式为json:
{
"reqType":0,
"perception": {
"inputText": {
"text": "附近的酒店"
},
"inputImage": {
"url": "imageUrl"
},
"selfInfo": {
"location": {
"city": "北京",
"province": "北京",
"street": "信息路"
}
}
},
"userInfo": {
"apiKey": "",
"userId": ""
}
}
②构建json格式的请求
还记得上面解析成ReceiveEntity 实例的xml信息吗,把content传进参数就可以了。
代码中userId是注册图灵账号的个人唯一id,apiKey创建机器人后可得到。下面注释的是非必须的参数。reqType也是非必须的。
private String getRequestJsonParam(String text) { // 构造JSON请求参数
JSONObject root = new JSONObject();
root.put("reqType", 0);
JSONObject perception = new JSONObject();
JSONObject inputText = new JSONObject();
inputText.put("text", text);
perception.put("inputText", inputText);
root.put("perception", perception);
JSONObject userInfo = new JSONObject();
userInfo.put("apiKey", apiKey);
userInfo.put("userId", userId);
root.put("userInfo", userInfo);
// JSONObject selfInfo = new JSONObject();
// JSONObject location = new JSONObject();
// location.put("city", "xx");
// location.put("province", "xx");
// location.put("street", "xx");
// selfInfo.put("location", location);
// perception.put("selfInfo", selfInfo);
return root.toString();
}
③发送请求
借助HttpClient创建POST请求,url是文档给出的接口地址http://openapi.tuling123.com/openapi/api/v2
,param是步骤②封装好的json参数。
public static String sendPost(String url, String param) {
// 创建Post请求
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader("Content-Type", "application/json;charset=utf8");
String res = "";
CloseableHttpResponse response = null;
try {
//装填参数
httpPost.setEntity(new ByteArrayEntity(param.getBytes("UTF-8")));
//执行post请求
response = HttpClients.createDefault().execute(httpPost);
//获取响应实体
if (response != null && response.getStatusLine().getStatusCode() == 200) {
res = EntityUtils.toString(response.getEntity());
}
return res;
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
(3)解析图灵响应
①图灵响应信息格式
查看文档,返回的格式如下:
{
"intent": {
"code": 10005,
"intentName": "",
"actionName": "",
"parameters": {
"nearby_place": "酒店"
}
},
"results": [
{
"groupType": 1,
"resultType": "url",
"values": {
"url": "http://m.elong.com/hotel/0101/nlist/#indate=2016-12-10&outdate=2016-12-11&keywords=%E4%BF%A1%E6%81%AF%E8%B7%AF"
}
},
{
"groupType": 1,
"resultType": "text",
"values": {
"text": "亲,已帮你找到相关酒店信息"
}
}
]
}
我们需要拿到的是results下面的text
②解析图灵响应的json
为了方便,我同样构建了个pojo存放results下面的信息。
public class ResultEntity {
private int groupType;
private String resultType;
private Map<String,String> values;
//getter、setter...
private String parseResponse(String resp) {
if (StringUtils.isEmpty(resp)) {
return "";
}
JSONObject jsonObject = JSONObject.parseObject(resp);
JSONArray array = jsonObject.getJSONArray("results");
String res = "";
for (int i = 0; i < array.size(); i++) {
JSONObject obj = array.getJSONObject(i);
ResultEntity entity = JSON.toJavaObject(obj, ResultEntity.class);
if (entity.getResultType().equals("text")) {
res = entity.getValues().get("text");
break;
}
}
return res;
}
好啦,现在拿到了结果了,赶紧给用户发过去吧
(4)封装微信响应,回复用户
①被动回复用户消息格式
回复文本消息格式:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content>
</xml>
②封装响应
public String getWeChatXmlFormatResponse(ReceiveEntity receiveEntity, String tuLinResult) {
String res = "<xml>\n"
+ " <ToUserName><![CDATA[" + receiveEntity.getFromUserName() + "]]></ToUserName>\n"
+ " <FromUserName><![CDATA[" + receiveEntity.getToUserName() + "]]></FromUserName>\n"
+ " <CreateTime>" + new Date().toString() + "</CreateTime>\n"
+ " <MsgType><![CDATA[text]]></MsgType>\n"
+ " <Content><![CDATA[" + tuLinResult + "]]></Content>\n"
+ "</xml>";
return res;
}
③返回
String res = weChatService.getWeChatXmlFormatResponse(receiveEntity, tuLinResult);
OutputStream outputStream = resp.getOutputStream();
outputStream.write(res.getBytes("UTF-8"));
outputStream.flush();
outputStream.close();
3.总结
现在已经可以在自己代码上测试了,下一节接入公众号和阿里云完成最后的步骤吧