Java开发微博粉丝服务(2)——消息推送服务

第二部分——消息推送服务

类型一:接受普通消息

类型二:接受事件消息

类型三:发送被动响应

项目树图。

注意:这个部分所用到的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": "私信或留言内容",

扫描二维码关注公众号,回复: 1549707 查看本文章

    "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

默认文案。subtypefollowunfollow时分别为关注事件消息取消关注事件消息;为subscribeunsubscribe时为触发订阅的私信关键词(如“dy”),非私信触发时(点击订阅按钮)为订阅事件消息取消订阅事件消息subtypescanscan_follow时为扫描二维码

data

string

消息内容

data:subtype

string

follow:关注事件,unfollow取消关注事件,subscribe订阅事件,unsubscribe订阅事件。scanscan_follow为二维码扫描事件。

data:key

string

subtypefollowunfollowsubscribeunsubscribe时不返回

data:ticket

string

subtypescanscan_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.	    }  

4、整个项目树图:


猜你喜欢

转载自blog.csdn.net/qq_38277570/article/details/80540860