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

目录

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

二、微信返回数据内容

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

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

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

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

 

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值