第二部分——消息推送服务
类型一:接受普通消息
类型二:接受事件消息
类型三:发送被动响应
项目树图。
注意:这个部分所用到的jar包有fastjson-1.2.47.jar(版本自定义,本人用的1.2.47)
一、接受普通消息
1、纯文本类型私信和留言消息:text
{ "type": "text", "receiver_id": 1902538057, "sender_id": 2489518277, "created_at": "Mon Jul 16 18:09:20 +0800 2012", "text": "私信或留言内容", "data": {} } |
||
返回值说明 |
||
属性 |
值的类型 |
说明描述 |
type |
string |
text |
receiver_id |
int64 |
消息的接收者 |
sender_id |
int64 |
消息的发送者 |
created_at |
string |
消息创建时间 |
text |
string |
私信内容 |
data |
string |
消息内容,纯文本私信或留言为空 |
当用户向微博服务器发送消息,微博消息会推送到开发者填写的URL地址上(json),所以URL会返回一个json的数据包,获取消息就是将消息从该个json的数据包中提取出来。下边就在cn.json.weibo包下新建一个名位RequestMethod的java文件,该个类的作用是获取request返回的数据,代码具体内容如下:
①RequestMethod.java
1. package cn.json.weibo; 2. 3. public class RequestMethod { 4. /** 5. * 获取request返回的JSON字符串数据包 6. */ 7. public static String getRequestJSON(HttpServletRequest request) throws IOException { 8. String submitMehtod = request.getMethod(); 9. //判断request返回的方式是GET 还是 POST 10. if (submitMehtod.equals("GET")) { 11. return new String(request.getQueryString().getBytes("iso-8859-1"),"utf-8").replaceAll("%22", "\""); 12. } else { 13. return getRequestPost(request); 14. } 15. } 16. /** 17. * 获取POST 请求内容 ,并且返回一个字符串 18. */ 19. public static String getRequestPost(HttpServletRequest request)throws IOException { 20. byte buffer[] = getRequestPostBytes(request); 21. String charEncoding = request.getCharacterEncoding(); 22. if (charEncoding == null) { 23. charEncoding = "UTF-8"; 24. } 25. return new String(buffer, charEncoding); 26. } 27. /** 28. * 获取 POST 请求的 byte[] 数组 29. */ 30. public static byte[] getRequestPostBytes(HttpServletRequest request)throws IOException { 31. int contentLength = request.getContentLength(); 32. if(contentLength<0){ 33. return null; 34. } 35. byte buffer[] = new byte[contentLength]; 36. for (int i = 0; i < contentLength;) { 37. int readlen = request.getInputStream().read(buffer, i, contentLength - i); 38. if (readlen == -1) { 39. break; 40. } 41. i += readlen; 42. } 43. return buffer; 44. } 45. }
②WeiBo.java下doPost方法内容如下:
1. public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { 2. //设定字符集 3. request.setCharacterEncoding("UTF-8"); 4. response.setCharacterEncoding("UTF-8"); 5. PrintWriter out = response.getWriter(); 6. //获取返回的数据 7. String requestJSON = RequestMethod.getRequestJSON(request); 8. System.out.println(requestJSON); 9. //将JSON字符串转换为集合 10. Map<?, ?> map = JSON.parseObject(requestJSON); 11. //获取字段为receiver_id的内容 12. String receiver_id = map.get("receiver_id").toString(); 13. String sender_id = map.get("sender_id").toString(); 14. String type = map.get("type").toString(); 15. String text = map.get("text").toString(); 16. }
2、位置类型私信消息:position
{ "type": "position", "receiver_id": 1902538057, "sender_id": 2489518277, "created_at": "Mon Jul 16 18:09:20 +0800 2012", "text": "我在这里: http://t.cn/zQgLLYO", "data": { "longitude": "116.308586", "latitude": "39.982525" } } |
||
返回值说明 |
||
属性 |
值的类型 |
说明描述 |
type |
string |
mention |
receiver_id |
int64 |
消息的接收者 |
sender_id |
int64 |
消息的发送者 |
created_at |
string |
消息创建时间 |
text |
string |
原位置私信文本,没有时用默认文案“发送了一个位置” |
data |
string |
消息内容 |
data:longitude |
string |
经度 |
data:latitude |
string |
纬度 |
从返回的数据格式来看data的值又是一个json类型的字符串,所以如果要取出里边的值还需要将data值的字符串转换成map集合,然后再获取里边的各个键和值。
①WeiBo.java下doPost方法内容如下:
1. public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { 2. //设定字符集 3. request.setCharacterEncoding("UTF-8"); 4. response.setCharacterEncoding("UTF-8"); 5. PrintWriter out = response.getWriter(); 6. //获取返回的数据 7. String requestJSON = RequestMethod.getRequestJSON(request); 8. System.out.println(requestJSON); 9. //将JSON字符串转换为集合 10. Map<?, ?> map = JSON.parseObject(requestJSON); 11. //获取字段为receiver_id的内容 12. String receiver_id = map.get("receiver_id").toString(); 13. String sender_id = map.get("sender_id").toString(); 14. String type = map.get("type").toString(); 15. String text = map.get("text").toString(); 16. String data = map.get("data").toString(); 17. //再次将data里边的数据转换为map集合 18. Map<?, ?> position_data = JSON.parseObject(data); 19. System.out.println(type); 20. //从该集合获取latitude的值 21. System.out.println("经度:" + position_data.get("latitude").toString()); 22. System.out.println("纬度:" + position_data.get("longitude").toString()); 23. }
3、语音类型私信和留言消息:voice
该个部分省略
4、图片类型私信和留言消息:image
该个部分省略
二、接受事件消息
1、事件消息:event
{ "type": "event", "receiver_id": 1902538057, "sender_id": 2489518277, "created_at": "Mon Jul 16 18:09:20 +0800 2012", "text": "事件消息", "data": { "subtype": "EVENT", "key": "EVENT_KEY", "ticket": "TICKET", } } |
||
返回值说明 |
||
属性 |
值的类型 |
说明描述 |
type |
string |
event |
receiver_id |
int64 |
消息的接收者 |
sender_id |
int64 |
消息的发送者 |
created_at |
string |
消息创建时间 |
text |
string |
默认文案。subtype为follow或unfollow时分别为“关注事件消息”、“取消关注事件消息”;为subscribe或unsubscribe时为触发订阅的私信关键词(如“dy”),非私信触发时(点击订阅按钮)为“订阅事件消息”、“取消订阅事件消息”;subtype为scan或scan_follow时为“扫描二维码”; |
data |
string |
消息内容 |
data:subtype |
string |
follow:关注事件,unfollow取消关注事件,subscribe订阅事件,unsubscribe订阅事件。scan和scan_follow为二维码扫描事件。 |
data:key |
string |
subtype为follow、unfollow、subscribe或unsubscribe时不返回 |
data:ticket |
string |
subtype为scan和scan_follow时才返回 |
类似上边的纯文本消息和位置消息,这里不再赘述。
2、被@消息:mention
该个部分省略
三、发送被动响应
该个部分应该算是比较重要的。
对于每一个POST请求,开发者在响应包(Get)中返回特定JSON包,对该消息进行响应。微博服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次;
如果发送被动响应消息,则返回JSON格式如下: 注意:JSON格式中的data字段内容必须进行UTF8格式的URLEncode
1 2 3 4 5 6 7 |
{ "result": true, "receiver_id":123456, "sender_id":123123, "type": "text", "data":"{}" } |
发送消息的总体大框架就是这几个参数,所以先对这集合基本的进行封装。
新建cn.json.weibo.message包,在该包下新建一个Message类,这个类是对主体公共框架的封装,具体代码如下:
①Message.java
1. package cn.json.weibo.message; 2. 3. public class Message { 4. /** 5. * 属性 值的类型 说明描述 6. * type string text 7. * receiver_id int64 消息的接收者 8. * sender_id int64 消息的发送者 9. * text string 私信内容 10. * data string 消息内容,纯文本私信或留言为空 11. */ 12. private boolean result; 13. private String receiver_id; 14. private String sender_id; 15. private String type; 16. private String data; 17. 18. public boolean isResult() { 19. return result; 20. } 21. public void setResult(boolean result) { 22. this.result = result; 23. } 24. public String getType() { 25. return type; 26. } 27. public void setType(String type) { 28. this.type = type; 29. } 30. public String getReceiver_id() { 31. return receiver_id; 32. } 33. public void setReceiver_id(String receiver_id) { 34. this.receiver_id = receiver_id; 35. } 36. public String getSender_id() { 37. return sender_id; 38. } 39. public void setSender_id(String sender_id) { 40. this.sender_id = sender_id; 41. } 42. public String getData() { 43. return data; 44. } 45. public void setData(String data) { 46. this.data = data; 47. } 48. }
1、纯文本类型私信消息:text
{ "text": "纯文本回复" } |
|||
data参数支持的参数 |
|||
参数名称 |
值的类型 |
是否必填 |
说明描述 |
text |
string |
true |
要回复的私信文本内容。文本大小必须小于300个汉字。 |
举例:当data对应json为{"text": "纯文本响应"}时,则进行URLEncode后对应data参数值为:"%7B%22text%22%3A%20%22%E7%BA%AF%E6%96%87%E6%9C%AC%E5%93%8D%E5%BA%94%22%7D%20"。
这一句话很关键,当初开发就是因为这样一句话折腾很久。为了区分,我建的类比较多,因为这个是纯文本消息,虽然只有一个参数,还是封装一下,在cn.json.weibo.message包下新建一个Text类,该个类是对data中text进行封装,Text.java具体代码如下:
①Text.java
1. package cn.json.weibo.message; 2. 3. public class Text { 4. private String text; 5. 6. public String getText() { 7. return text; 8. } 9. public void setText(String text) { 10. this.text = text; 11. } 12. }
到这里简单的纯文本封装结束。但是data里边的参数也是有多种的,所以data我们还需要单独封装,目前是纯文本,所以目前只封装一个属性,之后还会在这个基础之上进行修改。所以在cn.json.message包下新建一个Data类,用于封装data节点下的属性,Data.java的代码如下:
②Data.java
1. package cn.json.weibo.message; 2. 3. public class Data { 4. private String text; 5. 6. public String getText() { 7. return text; 8. } 9. public void setText(String text) { 10. this.text = text; 11. } 12. }
接着就是将实体类转换为微博指定的JSON数据包了。这里我们新建一个CreateMessage类,专门用来转换成JSON或者类似JSON的字符串,CreateMessage.java的代码如下:
③CreateMessage.java
1. package cn.json.weibo.message; 2. 3. import java.net.URLEncoder; 4. import com.alibaba.fastjson.JSONObject; 5. 6. public class CreateMessage { 7. /** 8. * 这个方法是公共拥有的消息类 9. * @param type 10. * @param data 11. * @return 12. */ 13. public static Message commonMessage(String type,String receiver_id,String sender_id,String data){ 14. Message message = new Message(); 15. message.setResult(true); 16. message.setType(type); 17. message.setReceiver_id(sender_id); 18. message.setSender_id(receiver_id); 19. message.setData(data); 20. return message; 21. } 22. /** 23. * 这个方法是创建纯文本消息 24. * @return 25. */ 26. public static String createTextMessage(String receiver_id,String sender_id,String text){ 27. String data = getData(text, null, null); 28. Message message = commonMessage(Message.TEXT,receiver_id,sender_id,data); 29. return JSONObject.toJSONString(message); 30. } 31. /** 32. * 这个方法是用来设置Data字段的内容,并且进行URLEncoder,返回一个字符串 33. * 这里设置了三个参数,使用方法是, 34. * 1、如果返回纯文本消息的话,将后边两个参数设置为null即可 35. * 2、如果返回图文信息,将第一个和第三个参数设置为null即可 36. * 3、如果返回位置信息,将第二个和第三个参数设置为null即可 37. * 2018-5-25 董尧 38. */ 39. public static String getData(String text,Articles[] articles,Position position){ 40. Data data = new Data(); 41. data.setText(text); 42. data.setArticles(articles); 43. if(position != null){ 44. data.setLongitude(position.getLongitude()); 45. data.setLatitude(position.getLatitude()); 46. } 47. String datastr = JSONObject.toJSONString(data); 48. try { 49. datastr = URLEncoder.encode(datastr, "UTF-8"); 50. } catch (Exception e) { 51. e.printStackTrace(); 52. } 53. return datastr; 54. } 55. }
④测试
在CreateMessage.java中添加主方法进行测试
1. public static void main(String[] args){ 2. String message = createTextMessage("123","456","测试"); 3. System.out.println(message); 4. }
控制台结果:
2、图文类型私信消息:articles
{ "articles": [ { "display_name": "两个故事", "summary": "今天讲两个故事,分享给你。谁是公司?谁又是中国人?", "image": "http://storage.mcp.weibo.cn/0JlIv.jpg", "url": "http://e.weibo.com/mediaprofile/article/detail?uid=1722052204&aid=983319" }, ... //最多支持8个图文,建议为1或3个 ] } |
|||
data参数支持的参数 |
|||
参数名称 |
值的类型 |
是否必填 |
说明描述 |
articles:display_name |
string |
true |
图文的显示名称标题 |
articles:summary |
string |
true |
图文的文字描述,大于等于2个图文时,仅显示第一个图文的描述 |
articles:image |
string |
true |
图文的缩略显示图片,需为JPG、PNG格式,单图及多图第一张推荐使用280*155,多图非第一张推荐使用64*64 |
articles:url |
string |
true |
图文的URL地址,点击后跳转 |
从官方给的实例来看,data参数里边还包含多个参数,而且数量还不是固定的,所以我们在对这个封装需要引入数组,由于图文里边的属性又和纯文本的属性不一样,所以需要把图文单独封装,此时在cn.json.weibo.message包下新建一个Articles类,该个类是进行封装图文的属性,Articles类的具体代码如下:
①Articles.java
1. package cn.json.weibo.message; 2. 3. public class Articles { 4. private String display_name; 5. private String summary; 6. private String image; 7. private String url; 8. 9. public String getDisplay_name() { 10. return display_name; 11. } 12. public void setDisplay_name(String display_name) { 13. this.display_name = display_name; 14. } 15. public String getSummary() { 16. return summary; 17. } 18. public void setSummary(String summary) { 19. this.summary = summary; 20. } 21. public String getImage() { 22. return image; 23. } 24. public void setImage(String image) { 25. this.image = image; 26. } 27. public String getUrl() { 28. return url; 29. } 30. public void setUrl(String url) { 31. this.url = url; 32. } 33. }
此外,由于图文的JSON数据也在data节点下,所以也需要将Articles封装进data中,此时需要修改Data.java,加入的代码如下:
②Data.java
1. public class Data { 2. ······ 3. private Articles[] articles; 4. 5. public Articles[] getArticles() { 6. return articles; 7. } 8. public void setArticles(Articles[] articles) { 9. this.articles = articles; 10. } 11. ······ 12. }
接着就是创建一条图文消息的方法了,这里往CreateMessage.java中添加如下方法,这个方法是用来创建一条图文消息
③CreateMessage.java
1. /** 2. * 这个方法是用来创建一个图文消息,返回一个图文类实例 3. * 调用该方法的四个参数分别是 4. * display_name string true 图文的显示名称标题 5. * summary string true 图文的文字描述,大于等于2个图文时,仅显示第一个图文的描述 6. * image string true 图文的缩略显示图片,需为JPG、PNG格式,单图及多图第一张推荐使用280*155,多图非第一张推荐使用64*64 7. * url string true 图文的URL地址,点击后跳转(注:该url必须为完整的url,例如, http://weibo.com/xxx ,如果省略掉”http:// “,则无法发送图文消息) 8. */ 9. public static Articles createArticle(String display_name,String summary,String image,String url){ 10. Articles article = new Articles(); 11. article.setDisplay_name(display_name); 12. article.setSummary(summary); 13. article.setImage(image); 14. article.setUrl(url); 15. return article; 16. }
有时候图文消息有多条,这时候我们就该考虑多条图文的封装,原理就是多次调用createArticle()方法,创建多条,接着就是将多条图文消息进行打包转换成图文数组,进而转换成JSON数据包形式,官方给出最多可以有8个图文,所以在这里我选择了用8个构造方法进行封装,这样应该调用的时候更加方便,在CreateMessage.java中加入如下代码:
④CreateMessage.java
1. /** 2. * 以下8个方法是用来封装图文消息 3. */ 4. public static Articles[] packageArticles(Articles art1){ 5. Articles[] arts = {art1}; 6. return arts; 7. } 8. public static Articles[] packageArticles(Articles art1,Articles art2){ 9. Articles[] arts = {art1,art2}; 10. return arts; 11. } 12. public static Articles[] packageArticles(Articles art1,Articles art2,Articles art3){ 13. Articles[] arts = {art1,art2,art3}; 14. return arts; 15. } 16. public static Articles[] packageArticles(Articles art1,Articles art2,Articles art3,Articles art4){ 17. Articles[] arts = {art1,art2,art3,art4}; 18. return arts; 19. } 20. public static Articles[] packageArticles(Articles art1,Articles art2,Articles art3,Articles art4,Articles art5){ 21. Articles[] arts = {art1,art2,art3,art4,art5}; 22. return arts; 23. } 24. public static Articles[] packageArticles(Articles art1,Articles art2,Articles art3,Articles art4,Articles art5,Articles art6){ 25. Articles[] arts = {art1,art2,art3,art4,art5,art6}; 26. return arts; 27. } 28. public static Articles[] packageArticles(Articles art1,Articles art2,Articles art3,Articles art4,Articles art5,Articles art6,Articles art7){ 29. Articles[] arts = {art1,art2,art3,art4,art5,art6,art7}; 30. return arts; 31. } 32. public static Articles[] packageArticles(Articles art1,Articles art2,Articles art3,Articles art4,Articles art5,Articles art6,Articles art7,Articles art8){ 33. Articles[] arts = {art1,art2,art3,art4,art5,art6,art7,art8}; 34. return arts; 35. }
接下来进行测试,在该类的主方法下添加如下代码:
1. Articles art1 = createArticle("测试", "测试", "http://storage.mcp.weibo.cn/0JlIv.jpg", "http://www.worldyao.cn/WeChat"); 2. Articles art2 = createArticle("测试2", "尽快寄哦", "http://storage.mcp.weibo.cn/0JlIv.jpg", "http://www.worldyao.cn/WeChat"); 3. String art_test = createArticlesMessage("123","456",packageArticles(art1, art2)); 4. System.out.println(art_test);
到这里,图文类消息封装完毕。
3、位置类型私信消息:position
{ "longitude": "116.308586", "latitude": "39.982525" } |
|||
data参数支持的参数 |
|||
参数名称 |
值的类型 |
是否必填 |
说明描述 |
longitude |
string |
true |
经度 |
latitude |
string |
true |
纬度 |
同样,我们将这个position进行封装,当然也可以不单独进行封装,具体的看代码。在cn.json.weibo.meassage包下新建一个Position类,这个类用来封装上边两个参数。代码如下:
①Position.java
1. package cn.json.weibo.message; 2. 3. public class Position { 4. private String longitude; 5. private String latitude; 6. 7. public String getLongitude() { 8. return longitude; 9. } 10. public void setLongitude(String longitude) { 11. this.longitude = longitude; 12. } 13. public String getLatitude() { 14. return latitude; 15. } 16. public void setLatitude(String latitude) { 17. this.latitude = latitude; 18. } 19. }
除此之外,这两个属性是data节点下的,所以还需要在Data.java下进行封装一次,所以Data.java的全部代码如下:
②Data.java
1. package cn.json.weibo.message; 2. 3. public class Data { 4. private String text; 5. 6. private String longitude; 7. private String latitude; 8. 9. private Articles[] articles; 10. 11. public String getText() { 12. return text; 13. } 14. public void setText(String text) { 15. this.text = text; 16. } 17. public Articles[] getArticles() { 18. return articles; 19. } 20. public void setArticles(Articles[] articles) { 21. this.articles = articles; 22. } 23. public String getLongitude() { 24. return longitude; 25. } 26. public void setLongitude(String longitude) { 27. this.longitude = longitude; 28. } 29. public String getLatitude() { 30. return latitude; 31. } 32. public void setLatitude(String latitude) { 33. this.latitude = latitude; 34. } 35. }
另外,添加方法,至于我为什么还需要将Position进行单独封装,就在下边,自己慢慢捉摸,当然可以可以不用单独封装。
1. /** 2. * 返回位置信息 3. * longitude string true 经度 4. * latitude string true 纬度 5. */ 6. public static String createPositionMessage(String receiver_id,String sender_id){ 7. Position position = new Position(); 8. position.setLongitude("114.926624"); 9. position.setLatitude("30.448897"); 10. String data = getData(null, null, position); 11. Message message = commonMessage(Message.POSITION,receiver_id,sender_id, data); 12. return JSONObject.toJSONString(message); 13. } 14. /** 15. * 这个方法是用来设置Data字段的内容,并且进行URLEncoder,返回一个字符串 16. * 这里设置了三个参数,使用方法是, 17. * 1、如果返回纯文本消息的话,将后边两个参数设置为null即可 18. * 2、如果返回图文信息,将第一个和第三个参数设置为null即可 19. * 3、入伙返回位置信息,将第二个和第三个参数设置为null即可 20. * 2018-5-25 董尧 21. */ 22. public static String getData(String text,Articles[] articles,Position position){ 23. Data data = new Data(); 24. data.setText(text); 25. data.setArticles(articles); 26. if(position != null){ 27. data.setLongitude(position.getLongitude()); 28. data.setLatitude(position.getLatitude()); 29. } 30. String datastr = JSONObject.toJSONString(data); 31. try { 32. datastr = URLEncoder.encode(datastr, "UTF-8"); 33. } catch (Exception e) { 34. e.printStackTrace(); 35. } 36. return datastr; 37. }