微信公众平台开发(一)——概述&微信消息处理

相关文档和链接

  1. 微信公众平台:https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html
  2. 登录公众号:https://mp.weixin.qq.com/
  3. 内网穿透工具:https://natapp.cn/
  4. 测试号申请平台:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Requesting_an_API_Test_Account.html

微信公众平台概述

微信公众平台是运营者通过公众号为微信用户提供资讯和服务的平台,而公众平台开放接口则是提供服务的基础,开发者在公众平台网站中创建公众号、获取接口权限后,可以通过阅读本接口文档来帮助开发。可以理解为微信公众平台开发就是开发者借助微信公众号,将自己的网站或者应用接入微信平台的服务。

公众号基本类型和区别

公众号类型:

1、企业号:微信为企业客户提供的移动应用入口、简化管理流程,提升组织协同动作效率;帮助企业建立员工、上下游供应链与企业IT系统间的连接。

适用人群:企业、政府、事业单位或其他组织。

2、服务号:服务号开放的接口比较多,主要针对于企业、以服务功能型为主的账号,功能强大,但不需要过多推送内容,以服务为主,给企业和组织提供更强大的服务与用户管理能力,帮助企业实现全新的公众号服务平台。很多企业也会选择服务号与订阅号同时建立来满足不同的需求主要用于服务。

适用人群:媒体、企业、政府或其他组织。

3、订阅号:主要用于推广。多是一些媒体、自媒体、公司市场、品牌、宣传使用,为媒体和个人提供一种新的信息传播方式,构建与读者之间更好的沟通和管理模式。订阅号还分个人订阅号和企业组织类的订阅号,个人号无法认证,请申请企业类的账号,才能获得更多权限和排名的优化。

适用人群:个人、媒体、政府或其他组织。

其中一些区别:
1、订阅号每个月发送30条信息,服务号是4条
2、订阅号不可以直接支付到商家自己,可以支付给第三方,服务号可以实现支付自己和第三方
3、订阅号拿不到用户数据,服务号可以
4、订阅号个人和公司都可以注册,服务号只能是公司

微信公众平台的基本功能

1、用户对话(订阅号、服务号)
2、自定义菜单(服务号、认证后订阅号、未认证订阅号<只能返回指定类型消息>)
3、多客服(认证订阅号、服务号)
4、卡券功能(消息提醒,转赠好友)(认证后服务号、订阅号)
5、推广(广告主、流量主)(认证后服务号、订阅号)
6、统计(用户、图文、接口、消息的分析)(服务号和订阅号)
7、微信小店(开通微信支付的服务号)
8、微信支付(认证服务号,目前开放部分订阅号的申请)
具体的功能权限参考:接口权限说明

准备工作

1. 搭建内⽹穿透环境

微信需要访问我们项⽬的web接⼝,在开发阶段可以把项⽬部署在公⽹能访问的云服务器上,也可以使⽤内⽹穿透
⼯具来访问我们⾃⼰电脑上运⾏的测试接⼝。
这⾥我们使⽤natapp作为内⽹穿透⼯具。

  • ⾸先在本站注册账号:https://natapp.cn/register

  • 登录后,点击左边 购买隧道,免费/付费均可
    在这里插入图片描述

  • 根据需要选择隧道协议,购买隧道

具体上手教程,查看文档:https://natapp.cn/article/natapp_newbie

2. 验证服务器url

官方文档:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html
登录微信测试号申请平台:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Requesting_an_API_Test_Account.html

填写服务器相关信息:
在这里插入图片描述

开发者提交信息后,微信服务器将发送 GET 请求到填写的服务器地址 URL 上,GET请求携带参数如下表所示:

在这里插入图片描述

开发者通过检验 signature 对请求进⾏校验(下⾯有校验⽅式)。若确认此次 GET 请求来⾃微信服务器,请原样返回 echostr 参数内容,则接⼊⽣效,成为开发者成功,否则接⼊失败。
加密/校验流程如下:

1)将token、timestamp、nonce三个参数进⾏字典序排序
2)将三个参数字符串拼接成⼀个字符串进⾏sha1加密
3)开发者获得加密后的字符串可与 signature 对⽐,标识该请求来源于微信
检验 signature 的Java示例代码:

	/**
	* 消息验证
	*
	* @param signature
	* @param timestamp
	* @param nonce
	* @param echostr
	* @return
	*/
	@GetMapping("/")
    public String check(String signature,
                        String timestamp,
                        String nonce,
                        String echostr){

        // 1)将token、timestamp、nonce三个参数进行字典序排序
        String token = "xxxxxxxxxxxxxx";
        List<String> list = Arrays.asList(token, timestamp, nonce);
        //排序
        Collections.sort(list);
        // 2)将三个参数字符串拼接成一个字符串进行sha1加密
        StringBuilder stringBuilder = new StringBuilder();
        for (String s : list) {
            stringBuilder.append(s);
        }
        //加密
        try {
            MessageDigest instance = MessageDigest.getInstance("sha1");
            //使用sha1进行加密,获得byte数组
            byte[] digest = instance.digest(stringBuilder.toString().getBytes());
            StringBuilder sum = new StringBuilder();
            for (byte b : digest) {
                sum.append(Integer.toHexString((b>>4)&15));
                sum.append(Integer.toHexString(b&15));
            }
            // 3)开发者获得加密后的字符串可与 signature 对比,标识该请求来源于微信
            if(!StringUtils.isEmpty(signature)&&signature.equals(sum.toString())){
                return echostr;
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

验证 URL 有效性成功后即接入生效,成为开发者。你可以在公众平台网站中申请微信认证,认证成功后,将获得更多接口权限,满足更多业务需求。

微信消息处理

1. 接收用户消息

接收消息说明

当普通微信⽤户向公众账号发消息时,微信服务器将 POST 消息的 XML 数据包到开发者填写的 URL 上。
请注意:

  • 关于重试的消息排重,推荐使用 msgid 排重。
  • 微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试。详情请见“发送消息 - 被动回复消息”。
  • 如果开发者需要对用户消息在5秒内立即做出回应,即使用“发送消息 - 被动回复消息”接口向用户被动回复消息时,可以在公众平台官网的开发者中心处设置消息加密。开启加密后,用户发来的消息和开发者回复的消息都会被加密(但开发者通过客服接口等 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>
  <MsgDataId>xxxx</MsgDataId>
  <Idx>xxxx</Idx>
</xml>

七种数据包结构详情查看官方文档:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html

接收消息并打印示例

    /**
     * 接收用户消息
     *
     * @param request
     * @return
     * @throws IOException
     */
    @PostMapping("/")
    public String receiveMessage(HttpServletRequest request) throws IOException {

        System.out.println("收到微信发来的消息");
        ServletInputStream inputStream = request.getInputStream();
        byte[] b = new byte[1024];
        int len = 0;
        while ((len = inputStream.read(b)) != -1) {
            System.out.println(new String(b, 0, len));
        }
        return "";
    }

执行结果:
在这里插入图片描述

将接收的xml消息封装成map

主要使用hutool工具包

 	/**
     * 接收用户消息
     *
     * @param request
     * @return
     * @throws IOException
     */
    @PostMapping("/")
    public String receiveMessage(HttpServletRequest request) throws IOException {

        System.out.println("收到微信发来的消息");
        // 将消息封装成map
        ServletInputStream inputStream = request.getInputStream();
        byte[] bytes = IoUtil.readBytes(inputStream);
        Map<String, Object> map = XmlUtil.xmlToMap(new String(bytes));
        System.out.println(map);
        return "";
    }

2. 回复⽤户消息

在回复用户消息时,使用的还是接收用户消息的方法,上面默认时回复了空字符串,如果想回复自定义内容,则需要将回复的消息封装成XML,然后在方法最后return回去。这就需要考虑到上述的六种数据包结构:
文本消息、图片消息、语音消息、视频消息、音乐消息、图文消息。因此需要根据获取消息事件(msgType)的触发使用switch进行对应消息的逻辑处理。

封装XML我们使用xstream工具包
引入下面坐标

        <dependency>
            <groupId>com.thoughtworks.xstream</groupId>
            <artifactId>xstream</artifactId>
            <version>1.4.11.1</version>
        </dependency>

文本消息XML封装类

代码如下:

package cn.kt.mywxdemo.message;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;

/**
 * <ToUserName><![CDATA[toUser]]></ToUserName>
 * <FromUserName><![CDATA[fromUser]]></FromUserName>
 * <CreateTime>12345678</CreateTime>
 * <MsgType><![CDATA[text]]></MsgType>
 * <Content><![CDATA[你好]]></Content>
 * <p>
 * Created by tao.
 * Date: 2023/3/7 15:26
 * 描述:
 */
@XStreamAlias("xml")
@Data
public class TextMessage {
    @XStreamAlias("ToUserName")
    private String toUserName;

    @XStreamAlias("FromUserName")
    private String fromUserName;

    @XStreamAlias("CreateTime")
    private long createTime;

    @XStreamAlias("MsgType")
    private String msgType;

    @XStreamAlias("Content")
    private String content;

}

图文消息消息封装类

package cn.kt.mywxdemo.message;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;

/**
 * @author Thor
 * @公众号 Java架构栈
 */
@XStreamAlias("item")
@Data
public class Article {
    @XStreamAlias("Title")
    private String title;

    @XStreamAlias("Description")
    private String description;

    @XStreamAlias("PicUrl")
    private String picUrl;

    @XStreamAlias("Url")
    private String url;
}



package cn.kt.mywxdemo.message;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;

import java.util.List;

/**
 * Created by tao.
 * Date: 2023/3/7 15:26
 * 描述:
 */
@XStreamAlias("xml")
@Data
public class NewsMessage {
    @XStreamAlias("ToUserName")
    private String toUserName;

    @XStreamAlias("FromUserName")
    private String fromUserName;

    @XStreamAlias("CreateTime")
    private long createTime;

    @XStreamAlias("MsgType")
    private String msgType;

    @XStreamAlias("ArticleCount")
    private int articleCount;

    @XStreamAlias("Articles")
    private List<Article> articles;
}

其他类型的XML消息封装类可以自己根据官方文档进行设置:六种数据包结构官方文档

需要注意的是@XStreamAlias(“自定义XML标签”) 标签的使用,需要和官方文档一一对应,一定要主要标签首字母的大小写,否则消息发送出错。
在这里插入图片描述

回复用户消息处理方法

以下处理了

  1. 字符串同义词消息回复
  2. 发送“图文”两个字获得图文消息回复
  3. 图片消息文字识别后回复文字
  4. 事件触发消息回复(自定义菜单事件、关注/取消关注事件、点击菜单跳转链接时的事件推送)

代码如下:

package cn.kt.mywxdemo.controller;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import cn.kt.mywxdemo.message.Article;
import cn.kt.mywxdemo.message.NewsMessage;
import cn.kt.mywxdemo.message.TextMessage;
import cn.kt.mywxdemo.utils.WordUtil;
import com.baidu.aip.ocr.AipOcr;
import com.thoughtworks.xstream.XStream;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.w3c.dom.Document;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * Created by tao.
 * Date: 2023/3/7 10:26
 * 描述:
 */
@RestController
public class WxController {

    public static final String APP_ID = "25xxxx99";
    public static final String API_KEY = "IAKeziT8Omxxxxx5gfHdNw";
    public static final String SECRET_KEY = "GoC65d2k01DuxxxxxwmTOVp4qVya";

    @GetMapping("/hello")
    public String hello() {
        return "hello wechat";
    }


    /**
     * 服务器接入微信公众平台
     *
     * @param signature
     * @param timestamp
     * @param nonce
     * @param echostr
     * @return
     */
    @GetMapping("/")
    public String check(String signature,
                        String timestamp,
                        String nonce,
                        String echostr) {

        // 1)将token、timestamp、nonce三个参数进行字典序排序
        String token = "xxxxxxxxxxxxxxxxx";
        List<String> list = Arrays.asList(token, timestamp, nonce);
        //排序
        Collections.sort(list);
        // 2)将三个参数字符串拼接成一个字符串进行sha1加密
        StringBuilder stringBuilder = new StringBuilder();
        for (String s : list) {
            stringBuilder.append(s);
        }

        //加密
        try {
            MessageDigest instance = MessageDigest.getInstance("sha1");
            //使用sha1进行加密,获得byte数组
            byte[] digest = instance.digest(stringBuilder.toString().getBytes());
            StringBuilder sum = new StringBuilder();
            for (byte b : digest) {
                sum.append(Integer.toHexString((b >> 4) & 15));
                sum.append(Integer.toHexString(b & 15));
            }
            // 3)开发者获得加密后的字符串可与 signature 对比,标识该请求来源于微信
            if (!StrUtil.isEmptyIfStr(signature) && signature.equals(sum.toString())) {
                return echostr;
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 接收用户消息
     *
     * @param request
     * @return
     * @throws IOException
     */
    @PostMapping("/")
    public String receiveMessage(HttpServletRequest request) throws IOException {
        // 将消息封装成map
        ServletInputStream inputStream = request.getInputStream();
        byte[] bytes = IoUtil.readBytes(inputStream);
        Map<String, Object> map = XmlUtil.xmlToMap(new String(bytes));
        System.out.println(map);

        //回复消息
        String msgType = map.get("MsgType").toString();
        String message = "";
        switch (msgType) {
            case "text":
                //回复同义词
                if ("图文".equals(map.get("Content"))) {
                    message = getReplyNewsMessage(map);
                } else {
                    message = getReplyMessageByWord(map);
                }
                break;
            case "event":
                message = handleEvent(map);
                break;
            case "image":
                message = handleImage(map);
                break;
            default:
                break;

        }
        return message;

    }


    /**
     * 获得回复的消息内容
     *
     * @param map
     * @return xml格式的字符串
     */
    private String getReplyMessage(Map<String, Object> map) {

        TextMessage textMessage = new TextMessage();
        textMessage.setToUserName(map.get("FromUserName").toString());
        textMessage.setFromUserName(map.get("ToUserName").toString());
        textMessage.setMsgType("text");
        textMessage.setContent("欢迎关注本公众号!");
        textMessage.setCreateTime(System.currentTimeMillis() / 1000);

        //XStream将Java对象转换成xml字符串
        XStream xStream = new XStream();
        xStream.processAnnotations(TextMessage.class);
        String xml = xStream.toXML(textMessage);
        return xml;
    }

    /**
     * 获得同义词
     *
     * @param map
     * @return xml格式的字符串
     */
    private String getReplyMessageByWord(Map<String, Object> map) {

        TextMessage textMessage = new TextMessage();
        textMessage.setToUserName(map.get("FromUserName").toString());
        textMessage.setFromUserName(map.get("ToUserName").toString());
        textMessage.setMsgType("text");
        String content = WordUtil.getWords(map.get("Content").toString());
        textMessage.setContent(content);

        textMessage.setCreateTime(System.currentTimeMillis() / 1000);

        //XStream将Java对象转换成xml字符串
        XStream xStream = new XStream();
        xStream.processAnnotations(TextMessage.class);
        String xml = xStream.toXML(textMessage);
        return xml;
    }


    /**
     * 获得图文消息
     *
     * @param map
     * @return xml格式的字符串
     */
    private String getReplyNewsMessage(Map<String, Object> map) {
        NewsMessage newsMessage = new NewsMessage();
        newsMessage.setToUserName(map.get("FromUserName").toString());
        newsMessage.setFromUserName(map.get("ToUserName").toString());
        newsMessage.setMsgType("news");
        newsMessage.setCreateTime(System.currentTimeMillis() / 1000);
        newsMessage.setArticleCount(1);
        List<Article> articles = new ArrayList<>();
        Article article = new Article();
        article.setTitle("左眼会陪右眼哭の博客");
        article.setDescription("干嘛这么想不开,要在脸上贴个输字!");
        article.setUrl("https://qkongtao.cn/");
        article.setPicUrl("https://upyun.qkongtao.cn/chevereto/2022/09/29/logo.png");
        articles.add(article);
        newsMessage.setArticles(articles);
        //XStream将Java对象转换成xml字符串
        XStream xStream = new XStream();
        xStream.processAnnotations(NewsMessage.class);
        String xml = xStream.toXML(newsMessage);
        return xml;
    }


    /**
     * 处理事件推送
     *
     * @param map
     * @return
     */
    private String handleEvent(Map<String, Object> map) {
        String event = map.get("Event").toString();
        switch (event) {
            case "CLICK":
                if ("1".equals(map.get("EventKey").toString())) {
                    TextMessage textMessage = new TextMessage();
                    textMessage.setToUserName(map.get("FromUserName").toString());
                    textMessage.setFromUserName(map.get("ToUserName").toString());
                    textMessage.setMsgType("text");
                    textMessage.setContent("你点击了event key是1的按钮");
                    textMessage.setCreateTime(System.currentTimeMillis() / 1000);
                    //XStream将Java对象转换成xml字符串
                    XStream xStream = new XStream();
                    xStream.processAnnotations(TextMessage.class);
                    String xml = xStream.toXML(textMessage);
                    return xml;
                }
                break;
            case "VIEW":
                System.out.println("view");
                break;
            // 用户已关注触发
            case "SCAN":
                System.out.println("SCAN");
                break;
            // 用户未关注,点击关注后触发
            case "subscribe":
                System.out.println("subscribe");
                break;
            default:
                break;

        }
        return null;
    }

    /**
     * 图片文字识别处理
     *
     * @param map
     * @return
     */
    private String handleImage(Map<String, Object> map) {
        // 初始化一个AipOcr
        AipOcr client = new AipOcr(APP_ID, API_KEY, SECRET_KEY);
        // 网络图片文字识别, 图片参数为远程url图片
        String url = map.get("PicUrl").toString();
        JSONObject res = client.webImageUrl(url, new HashMap<String, String>());
        //解析内容,生成返回的数据
        JSONArray wordsResult = res.getJSONArray("words_result");
        StringBuilder stringBuilder = new StringBuilder();
        Iterator<Object> iterator = wordsResult.iterator();
        while (iterator.hasNext()) {
            JSONObject jsonObject = (JSONObject) iterator.next();
            stringBuilder.append(jsonObject.getString("words") + " ");
        }
        return createTextMessage(stringBuilder.toString(), map);
    }

    private String createTextMessage(String content, Map<String, Object> map) {
        TextMessage textMessage = new TextMessage();
        textMessage.setToUserName(map.get("FromUserName").toString());
        textMessage.setFromUserName(map.get("ToUserName").toString());
        textMessage.setMsgType("text");
        textMessage.setContent(content);
        textMessage.setCreateTime(System.currentTimeMillis() / 1000);
        //XStream将Java对象转换成xml字符串
        XStream xStream = new XStream();
        xStream.processAnnotations(TextMessage.class);
        String xml = xStream.toXML(textMessage);
        return xml;
    }
}

其中图片消息,回复的是图片ORC文字识别处理的内容,使用到了baiduAi;
需要引入下面坐标:

        <dependency>
            <groupId>com.baidu.aip</groupId>
            <artifactId>java-sdk</artifactId>
            <version>4.16.12</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-simple</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

获取同义词使用到了HttpUtil 和 WordUtil;
需要引入下面坐标

        <!--httpClient需要的依赖-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.2</version>
        </dependency>
        <!--//httpclient缓存-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient-cache</artifactId>
            <version>4.5</version>
        </dependency>
        <!--//http的mime类型都在这里面-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpmime</artifactId>
            <version>4.3.2</version>
        </dependency>

        <dependency>
            <groupId>net.sf.json-lib</groupId>
            <artifactId>json-lib</artifactId>
            <version>2.2.3</version>
            <classifier>jdk15</classifier>
        </dependency>

HttpUtil代码如下:

package cn.kt.mywxdemo.utils;

import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Map;


public class HttpUtil {
    /**
     * get方式的http请求
     *
     * @param httpUrl 请求地址
     * @return 返回结果
     */
    public static String doGet(String httpUrl) {
        HttpURLConnection connection = null;
        InputStream inputStream = null;
        BufferedReader bufferedReader = null;
        String result = null;// 返回结果字符串
        try {
            // 创建远程url连接对象
            URL url = new URL(httpUrl);
            // 通过远程url连接对象打开一个连接,强转成httpURLConnection类
            connection = (HttpURLConnection) url.openConnection();
            // 设置连接方式:get
            connection.setRequestMethod("GET");
            // 设置连接主机服务器的超时时间:15000毫秒
            connection.setConnectTimeout(15000);
            // 设置读取远程返回的数据时间:60000毫秒
            connection.setReadTimeout(60000);
            // 发送请求
            connection.connect();
            // 通过connection连接,获取输入流
            if (connection.getResponseCode() == 200) {
                inputStream = connection.getInputStream();
                // 封装输入流,并指定字符集
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
                // 存放数据
                StringBuilder sbf = new StringBuilder();
                String temp;
                while ((temp = bufferedReader.readLine()) != null) {
                    sbf.append(temp);
                    sbf.append(System.getProperty("line.separator"));
                }
                result = sbf.toString();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            if (null != bufferedReader) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                connection.disconnect();// 关闭远程连接
            }
        }
        return result;
    }


    /**
     * post方式的http请求
     *
     * @param httpUrl 请求地址
     * @param param   请求参数
     * @return 返回结果
     */
    public static String doPost(String httpUrl, String param) {
        HttpURLConnection connection = null;
        InputStream inputStream = null;
        OutputStream outputStream = null;
        BufferedReader bufferedReader = null;
        String result = null;
        try {
            URL url = new URL(httpUrl);
            // 通过远程url连接对象打开连接
            connection = (HttpURLConnection) url.openConnection();
            // 设置连接请求方式
            connection.setRequestMethod("POST");
            // 设置连接主机服务器超时时间:15000毫秒
            connection.setConnectTimeout(15000);
            // 设置读取主机服务器返回数据超时时间:60000毫秒
            connection.setReadTimeout(60000);
            // 默认值为:false,当向远程服务器传送数据/写数据时,需要设置为true
            connection.setDoOutput(true);
            // 设置传入参数的格式:请求参数应该是 name1=value1&name2=value2 的形式。
//            connection.setRequestProperty("Content-Type", "application/json;charset=utf-8");
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            // 通过连接对象获取一个输出流
            outputStream = connection.getOutputStream();
            // 通过输出流对象将参数写出去/传输出去,它是通过字节数组写出的
            outputStream.write(param.getBytes());
            // 通过连接对象获取一个输入流,向远程读取
            if (connection.getResponseCode() == 200) {
                inputStream = connection.getInputStream();
                // 对输入流对象进行包装:charset根据工作项目组的要求来设置
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
                StringBuilder sbf = new StringBuilder();
                String temp;
                // 循环遍历一行一行读取数据
                while ((temp = bufferedReader.readLine()) != null) {
                    sbf.append(temp);
                    sbf.append(System.getProperty("line.separator"));
                }
                result = sbf.toString();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            if (null != bufferedReader) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != outputStream) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                connection.disconnect();
            }
        }
        return result;
    }

    /**
     * post方式的http请求
     *
     * @param httpUrl 请求地址
     * @param param   请求参数
     * @return 返回结果
     */
    public static String doPostByButton(String httpUrl, String param) {
        HttpURLConnection connection = null;
        InputStream inputStream = null;
        OutputStream outputStream = null;
        BufferedReader bufferedReader = null;
        String result = null;
        try {
            URL url = new URL(httpUrl);
            // 通过远程url连接对象打开连接
            connection = (HttpURLConnection) url.openConnection();
            // 设置连接请求方式
            connection.setRequestMethod("POST");
            // 设置连接主机服务器超时时间:15000毫秒
            connection.setConnectTimeout(15000);
            // 设置读取主机服务器返回数据超时时间:60000毫秒
            connection.setReadTimeout(60000);
            // 默认值为:false,当向远程服务器传送数据/写数据时,需要设置为true
            connection.setDoOutput(true);
            // 设置传入参数的格式:请求参数应该是 name1=value1&name2=value2 的形式。
            connection.setRequestProperty("Content-Type", "application/json;charset=utf-8");
            // 通过连接对象获取一个输出流
            outputStream = connection.getOutputStream();
            // 通过输出流对象将参数写出去/传输出去,它是通过字节数组写出的
            outputStream.write(param.getBytes());
            // 通过连接对象获取一个输入流,向远程读取
            if (connection.getResponseCode() == 200) {
                inputStream = connection.getInputStream();
                // 对输入流对象进行包装:charset根据工作项目组的要求来设置
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
                StringBuilder sbf = new StringBuilder();
                String temp;
                // 循环遍历一行一行读取数据
                while ((temp = bufferedReader.readLine()) != null) {
                    sbf.append(temp);
                    sbf.append(System.getProperty("line.separator"));
                }
                result = sbf.toString();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            if (null != bufferedReader) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != outputStream) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                connection.disconnect();
            }
        }
        return result;
    }

    //httpClient发送携带文件的post请求
    public static String doPostByFile(String url, Map<String, String> map, String localFile, String fileParamName) {
        HttpPost httpPost = new HttpPost(url);
        CloseableHttpClient httpClient = HttpClients.createDefault();
        String resultString = "";
        CloseableHttpResponse response = null;
        try {
            // 把文件转换成流对象FileBody
            FileBody bin = new FileBody(new File(localFile));
            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            builder.addPart(fileParamName, bin);
            if (map != null) {
                for (String key : map.keySet()) {
                    builder.addPart(key,
                            new StringBody(map.get(key), ContentType.create("text/plain", Consts.UTF_8)));
                }
            }
            HttpEntity reqEntity = builder.build();
            httpPost.setEntity(reqEntity);
            // 发起请求 并返回请求的响应
            response = httpClient.execute(httpPost, HttpClientContext.create());
            resultString = EntityUtils.toString(response.getEntity(), "utf-8");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (response != null)
                    response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return resultString;
    }
}

WordUtil代码如下:

package cn.kt.mywxdemo.utils;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;


public class WordUtil {

    //随机笑话
    public static final String WORD_URL = "http://apis.juhe.cn/tyfy/query?key=%s";

    //申请接口的请求key
    // TODO: 您需要改为自己的请求key
    public static final String KEY = "a6c3cc5627bxxxxxxxxxxxxxx890273ce";

    public static String getWords(String word) {
        //发送http请求的url
        String url = String.format(WORD_URL, KEY);
        final String response = HttpUtil.doPost(url, "word=" + word);
        System.out.println("接口返回:" + response);
        try {
            JSONObject jsonObject = JSONObject.fromObject(response);
            int error_code = jsonObject.getInt("error_code");
            if (error_code == 0) {
                System.out.println("调用接口成功");
                JSONObject result = jsonObject.getJSONObject("result");
                JSONArray words = result.getJSONArray("words");
                StringBuilder stringBuilder = new StringBuilder();
                words.stream().forEach(w -> stringBuilder.append(w + " "));
                System.out.println(stringBuilder);
                return stringBuilder.toString();
            } else {
                System.out.println("调用接口失败:" + jsonObject.getString("reason"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

用户消息回复效果如下

  1. 字符串同义词消息回复
  2. 发送“图文”两个字获得图文消息回复
  3. 图片消息文字识别后回复文字
  4. 事件触发消息回复(自定义菜单事件、关注/取消关注事件、点击菜单跳转链接时的事件推送)

在这里插入图片描述

源码下载

源码链接:https://gitee.com/qkongtao/my-wx-demo

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不愿意做鱼的小鲸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值