欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

八、快速上手处理微信请求:接收、解析、处理微信后台发送给你服务器的消息(一)

程序员文章站 2023-12-24 11:44:33
...

目录

一、官方内容解释~~~飞机票

二、微信返回数据内容

三 、接收、解析微信请求数据

1、controller层

 2、service层

①、解析微信消息处理成map对象

②、去除重复请求

③、记录日志

④、处理不同类型的消息请求

⑤、自定义操作


很多人对于微信的请求处理一直弄不太明白,其实难,主要是微信返回的数据类型太多了,解析器了比较绕(微信文档全是坑就对了)

文章中的代码保证可以使用,提供完整的代码哦

一、官方内容解释~~~飞机票

  1. 微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试。详情请见“发送消息-被动回复消息”。

  2. 如果开发者需要对用户消息在5秒内立即做出回应,即使用“发送消息-被动回复消息”接口向用户被动回复消息时,可以在

  3. 关于重试的消息排重,推荐使用msgid排重。

 

PS:一定要保证及时响应微信哦,如果不能及时响应,一定要处理掉微信多次推送带来的消息重叠,不然用户会收到这种不友好的提示。

八、快速上手处理微信请求:接收、解析、处理微信后台发送给你服务器的消息(一) 

二、微信返回数据内容

PS:此内容只是示例,因返回数据类型不同参数会有一定出入,具体数据见每个接口的内容解释,其他内容不再赘述,详见微信官方文档解释。直奔主题吧

<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>

 

参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 消息类型,文本为text
Content 文本消息内容
MsgId 消息id,64位整型

三 、接收、解析微信请求数据

1、controller层

接收请求的方法的参数只有两个
HttpServletRequest request
HttpServletResponse response
文中如果出现city 可以忽略,我得demo是多个公众号一起用的,city仅作为公众号的识别代码用的,sequence是一个雪花算法的随机数,作为唯一识别码,可以忽略或者写1L传递即可
为了方便理解,下文的代码我不提取单独的变量了,使用的地方我尽量直接拿,以防止看起来乱

        boolean isGet = request.getMethod().toLowerCase().equals("get");
········PrintWriter out;
        try {
            out = response.getWriter();
            if(isGet) {
                // 微信加密签名
                String signature = request.getParameter("signature");
                // 时间戳
                String timestamp = request.getParameter("timestamp");
                // 随机数
                String nonce = request.getParameter("nonce");
                // 随机字符串
                String echostr = request.getParameter("echostr");
                // 通过校验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
                if(SignUtil.checkSignature(token, signature, timestamp, nonce)) {
                    out.print(echostr);
                    out.flush();
                    out.close();
                    out = null;
                }
            } else {
                String respXml = messageService.processRequest(request, city);
                logger.debug("WeChatServiceController.replyMessage:respXml = " + respXml + "请求序列:");

                out.print(respXml);
                out.flush();
                out.close();
                out = null;
            }
        } catch (Exception e1) {
            logger.error("系统异常", e1);
        }

验签方法 checkSignature(不太建议使用三目运算符,个人感觉数量级上来判断比较慢,写demo图省事

public static boolean checkSignature(String token, String signature, String timestamp, String nonce) {
        String[] arra = new String[]{token, timestamp, nonce};
        //将token,timestamp,nonce组成数组进行字典排序 
        Arrays.sort(arra);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < arra.length; i++) {
            sb.append(arra[i]);
        }
        MessageDigest md = null;
        String stnStr = null;
        try {
            md = MessageDigest.getInstance("SHA-1");
            byte[] digest = md.digest(sb.toString().getBytes());
            stnStr = byteToStr(digest);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        sb = null;
        return stnStr != null ? stnStr.equals(signature.toUpperCase()) : false;
    }

 2、service层

回调事件处理接口

 public String processRequest(HttpServletRequest request);

这个接口要做几件事

①、解析微信消息处理成map对象

第一步就是把微信返回的数据处理成方便我们解析的消息类型,我们这里解析成Map

        Map<String,String> wechatMap = parseXml(request);//调用处 记得判空

    public static Map<String,String> parseXml(HttpServletRequest request) throws Exception {
        // 将解析结果存储在HashMap中
        Map<String,String> map = new HashMap();
        // 从request中取得输入流
        InputStream inputStream = request.getInputStream();
        // 读取输入流
        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        // 得到xml根元素
        Element root = document.getRootElement();
        // 得到根元素的所有子节点
        List<Element> elementList = root.elements();
        // 遍历所有子节点
        for (Element e : elementList)
            map.put(e.getName(), e.getText());
        // 释放资源
        inputStream.close();
        inputStream = null;
        return map;
    }

②、去除重复请求

消息重复内容拦截,利用缓存锁来拦截重复的消息内容,返回空内容在controller层以处理可以返回空字符串

if (StringUtils.isBlank(wechatMap.get("MsgId")) && wechatMap.containsKey("Ticket")) {
  msgid = wechatMap.get("Ticket") + wechatMap.get("CreateTime");
}
if (StringUtils.isNotBlank(msgid) && !lockService.addLock(CODE_LOCK, fromUserName, msgid)) {
   logger.warn("msgid + "重复请求已处理");
   return "";
}

③、记录日志

根据业务需求的不同可以考虑记录微信请求的数据内容。

④、处理不同类型的消息请求

这应该是个比较大的交互模块,下文采取一种自定义注解的形式来处理,并只阐述大体内容,具体方法后文给出链接可供使用

首先 我们拿到两个重要的参数

//微信返回类型
String msgType = wechatMap.get("MsgType");
// 事件类型
String eventType = wechatMap.get("Event");

一种处理方式是常规的处理,将所有的返回类型和事件类型枚举出来,然后在不同的if结构体去做不同的操作,作者没有采取这种方式。

八、快速上手处理微信请求:接收、解析、处理微信后台发送给你服务器的消息(一) 

第二种是用的自定义注解的形式处理的,包括后文的扫码和文本处理都采用这种方式 

//以返回类型+事件类型形成一个唯一的类型KEY
String messageType = msgType + msgType;
//直接传入自定义注解处理类,靠注解去寻找不同的接口。想使用spring的注解也可以。
 MessageClassifyService merchantRegisterBean = MessageBeanContextUtil.getmessageTypeBean(messageType);
if (null == merchantRegisterBean) {
   //TODO 异常处理
   return null;
}
//wechatMap是上文解析的map参数
MessageDtoOut out = merchantRegisterBean.messageDispose(wechatMap);

 MessageClassifyService.java接口

/**
 * @version V1.0.0
 * @Description 消息分类处理基类接口
 * @Author 
 * @Package 
 * @CreateTime 
 */
public interface MessageClassifyService {
    /**
     * 基础接口
     *
     * @param wechatMap 微信返回信息
     * @return
     */
    public MessageDtoOut messageDispose(Map<String,String> wechatMap,long sequence);
}

MessageBaseServiceImpl.java这是基础接口实现类,当找不到对应接口的时候可以跳转到这个进行返回,(实际用起来意义不大,强迫症容错用的) 

public class MessageBaseServiceImpl implements MessageClassifyService {
    private final static Logger logger = Logger.getLogger(MessageBaseServiceImpl.class);
    /**
     * 基础接口
     *
     * @param wechatMap 微信返回信息
     * @param object    用户自定义数据
     * @return
     */
    @Override
    public MessageDtoOut messageDispose(Map<String,String> wechatMap, long sequence) {
        logger.warn("传入参数:wechatMap = [" + wechatMap + "],, sequence = [" + sequence + "]");
        return new MessageDtoOut("您正在访问消息路径,请勿重复发送数据");
    }
}

我们拿文本消息接口来举例(文本消息接口,用户在公众号发送文字,微信就会推送该事件到我们服务器)

 MessTextServiceImpl.java文本消息处理实现类和图像处理类

MessageStaticType.MESSAGE_TEXT
该参数实际上就是一个静态常量,列出了所有微信的状态组合~~以返回类型+事件类型形成一个唯一的类型KEY

这样注解就能通过前文组成的key直接找到对应的实现类,一个接口对应多个实现类,只需要改变实现类的getMessageType 值就可以自动跳到不同的实现类

@MessageSelector(getMessageType =  MessageStaticType.MESSAGE_TEXT)
public class MessTextServiceImpl implements MessageClassifyService {
    private final static Logger logger = Logger.getLogger(MessTextServiceImpl.class);
     /**
     * 基础接口
     *
     * @param wechatMap 微信返回信息
     * @param object    用户自定义数据
     * @return
     */
    @Override
    public MessageDtoOut messageDispose(Map<String,String> wechatMaplong sequence) {
        //处理微信消息内容
        //具体处理方法在消息处理讲解
    }
}

 

@MessageSelector(getMessageType = MessageStaticType.MESSAGE_IMAGE)
public class MessImageServiceImpl implements MessageClassifyService {
    private final static Logger logger = Logger.getLogger(MessImageServiceImpl.class);
    /**
     * 基础接口
     *
     * @param wechatMap 微信返回信息
     * @param object    用户自定义数据
     * @return
     */
    @Override
    public MessageDtoOut messageDispose(Map<String,String> wechatMap,long sequence) {
        return new MessageDtoOut("测试-您发送的是图片,请勿重复发送数据");
    }
}

 八、快速上手处理微信请求:接收、解析、处理微信后台发送给你服务器的消息(一)

 

⑤、自定义操作

四、重点的处理类demo

列举三个重点实现类的内容处理

关注接口处理类 、扫码接口处理类、文字消息处理类   见下文

 

相关标签: 微信 公众号

上一篇:

下一篇: