为什么微信消息重复提醒的消息MsgId出现重复了,无法排重了

搜索结果部分由为什么微信的消息MsgId出现重复了,无法排重了。。
以前对消息排重是没问题的,MsgId不是重复的,可是今天测试发现,居然微信服务器发过来的有几次不同消息内容的MsgId居然是一样的,这种情况该怎么排重,排重的话会把其它一两次不同消息的给排除掉的,那样的话就接收不到了。。急。。。
(527 威望)
请输入验证码:
[captcha placeholder]
或 后不会被要求输入验证码。
请输入验证码:
&&&& 或 后不会被要求输入验证码。
1、关于重试的消息排重,推荐使用msgid排重。
2、微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试。详情请见“发送消息-被动回复消息”。
根据官方文档的话,MsgId应该是不会重复的。。。
这种情况你只能问微信的技术人员了。。。以微信的技术,应该是不至于把id搞重复的。。所以有没有可能是别的问题?
或者有没有可能中间人攻击? 消息篡改,伪造?
(986 威望)
请输入验证码:
[captcha placeholder]
或 后不会被要求输入验证码。
提一个问题:
(1,114 威望)
(1,116 威望)
(1,025 威望)
(1,029 威望)
2015年 9月16日
(1,108 威望)
欢迎访问随意问技术百科,为了给您提供更好的服务,请及时反馈您的意见。本帖子已过去太久远了,不再提供回复功能。微信中的消息类型有:文本,图片,语音,视频,地理位置,链接和事件消息。除了事件消息外,其他的统称为普通消息。微信中消息的推送与响应都是以xml数据包传输的。在用户发送消息给公众号时,微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。普通消息可以使用msgid排重,以避免重复的消息对业务逻辑的影响。
假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此座任何处理,并且不会发起重试。需要注意的是:这里说的回复空串并不是回复空的文本消息,而是直接Response.Write(&&)即可。
下面简要对各普通消息说明一下。
&ToUserName&&![CDATA[toUser]]&&/ToUserName&
&FromUserName&&![CDATA[fromUser]]&&/FromUserName&
&CreateTime&&/CreateTime&
&MsgType&&![CDATA[text]]&&/MsgType&
&Content&&![CDATA[this is a test]]&&/Content&
&MsgId&3456&/MsgId&
&ToUserName&&![CDATA[toUser]]&&/ToUserName&
&FromUserName&&![CDATA[fromUser]]&&/FromUserName&
&CreateTime&&/CreateTime&
&MsgType&&![CDATA[image]]&&/MsgType&
&PicUrl&&![CDATA[this is a url]]&&/PicUrl&
&MediaId&&![CDATA[media_id]]&&/MediaId&
&MsgId&3456&/MsgId&
&ToUserName&&![CDATA[toUser]]&&/ToUserName&
&FromUserName&&![CDATA[fromUser]]&&/FromUserName&
&CreateTime&&/CreateTime&
&MsgType&&![CDATA[voice]]&&/MsgType&
&MediaId&&![CDATA[media_id]]&&/MediaId&
&Format&&![CDATA[Format]]&&/Format&
&MsgId&3456&/MsgId&
&ToUserName&&![CDATA[toUser]]&&/ToUserName&
&FromUserName&&![CDATA[fromUser]]&&/FromUserName&
&CreateTime&&/CreateTime&
&MsgType&&![CDATA[video]]&&/MsgType&
&MediaId&&![CDATA[media_id]]&&/MediaId&
&ThumbMediaId&&![CDATA[thumb_media_id]]&&/ThumbMediaId&
&MsgId&3456&/MsgId&
地理位置消息:
&ToUserName&&![CDATA[toUser]]&&/ToUserName&
&FromUserName&&![CDATA[fromUser]]&&/FromUserName&
&CreateTime&&/CreateTime&
&MsgType&&![CDATA[location]]&&/MsgType&
&Location_X&23.134521&/Location_X&
&Location_Y&113.358803&/Location_Y&
&Scale&20&/Scale&
&Label&&![CDATA[位置信息]]&&/Label&
&MsgId&3456&/MsgId&
&ToUserName&&![CDATA[toUser]]&&/ToUserName&
&FromUserName&&![CDATA[fromUser]]&&/FromUserName&
&CreateTime&&/CreateTime&
&MsgType&&![CDATA[link]]&&/MsgType&
&Title&&![CDATA[公众平台官网链接]]&&/Title&
&Description&&![CDATA[公众平台官网链接]]&&/Description&
&Url&&![CDATA[url]]&&/Url&
&MsgId&3456&/MsgId&
细心的程序猿应该发现了,所有的消息中(包括事件消息),都包含下面几个字段
ToUserName
接收方微信号
FromUserName
发送方微信号,若为普通用户,则是一个OpenID
CreateTime
消息创建时间
而消息的类型在文章开头已经讲了,分别是:文本(text),图片(image),语音(voice),视频(video),地理位置(location),链接(link),事件(event)
为了方便管理和代码编写,我们可以把这些消息类型写一个枚举。如下:
/// &summary&
/// 消息类型枚举
/// &/summary&
public enum MsgType
/// &summary&
///文本类型
/// &/summary&
/// &summary&
/// 图片类型
/// &/summary&
/// &summary&
/// 语音类型
/// &/summary&
/// &summary&
/// 视频类型
/// &/summary&
/// &summary&
/// 地理位置类型
/// &/summary&
/// &summary&
/// 链接类型
/// &/summary&
/// &summary&
/// 事件类型
/// &/summary&
这里说明下,C#中event是关键字,所以event在枚举中就不能使用了,所以为了统一,我这里的枚举全部使用大写的。
既然所有的消息体都有上面的几个字段,那就可以写一个基类,然后不同的消息实体继承这个基类。(一直在纠结一个问题,以前我都是将所有的消息体中的字段写在一个类中,调用起来也很方便,只是类中的字段越来越多,看着都不爽。再加上本人才疏学浅,面向对象也使用的不熟练,所以一直都是在一个类中罗列所有的字段
调用的时候直接&& var ss = WeiXinRequest.RequestHelper(token, EncodingAESKey, appid);
返回一个WeiXinRequest,然后再对消息类型和事件类型判断,做出响应。
今天重新做了下调整,也就是分了子类基类,代码可读性提高了,调用起来却没有之前方便了,各位朋友给点建议呗。
下面是各消息实体
public abstract class BaseMessage
/// &summary&
/// 开发者微信号
/// &/summary&
public string ToUserName { get; set; }
/// &summary&
/// 发送方帐号(一个OpenID)
/// &/summary&
public string FromUserName { get; set; }
/// &summary&
/// 消息创建时间 (整型)
/// &/summary&
public string CreateTime { get; set; }
/// &summary&
/// 消息类型
/// &/summary&
public MsgType MsgType { get; set; }
public virtual void ResponseNull()
Utils.ResponseWrite("");
public virtual void ResText(EnterParam param, string content)
/// &summary&
/// 回复消息(音乐)
/// &/summary&
void ResMusic(EnterParam param, Music mu)
void ResVideo(EnterParam param, Video v)
/// &summary&
/// 回复消息(图片)
/// &/summary&
void ResPicture(EnterParam param, Picture pic, string domain)
/// &summary&
/// 回复消息(图文列表)
/// &/summary&
/// &param name="param"&&/param&
/// &param name="art"&&/param&
void ResArticles(EnterParam param, List&Articles& art)
/// &summary&
/// 多客服转发
/// &/summary&
/// &param name="param"&&/param&
void ResDKF(EnterParam param)
/// &summary&
/// 多客服转发如果指定的客服没有接入能力(不在线、没有开启自动接入或者自动接入已满),该用户会一直等待指定客服有接入能力后才会被接入,而不会被其他客服接待。建议在指定客服时,先查询客服的接入能力指定到有能力接入的客服,保证客户能够及时得到服务。
/// &/summary&
/// &param name="param"&用户发送的消息体&/param&
/// &param name="KfAccount"&多客服账号&/param&
void ResDKF(EnterParam param, string KfAccount)
void Response(EnterParam param, string data)
基类中定义了消息体的公共字段,以及用于响应用户请求的虚方法(响应消息不是本文重点,所以方法体就没有贴出来,请关注后续文章)。
基类中方法的参数有个是EnterParam类型的,这个类是用户接入时和验证消息真实性需要使用的参数,包括token,加密密钥,appid等。定义如下:
/// &summary&
/// 微信接入参数
/// &/summary&
public class EnterParam
/// &summary&
/// 是否加密
/// &/summary&
public bool IsAes { get; set; }
/// &summary&
/// 接入token
/// &/summary&
public string token { get; set; }
/// &summary&
///微信appid
/// &/summary&
public string appid { get; set; }
/// &summary&
/// 加密密钥
/// &/summary&
public string EncodingAESKey { get; set; }
文本实体:
public class TextMessage:BaseMessage
/// &summary&
/// 消息内容
/// &/summary&
public string Content { get; set; }
/// &summary&
/// 消息id,64位整型
/// &/summary&
public string MsgId { get; set; }
图片实体:
public class ImgMessage : BaseMessage
/// &summary&
/// 图片路径
/// &/summary&
public string PicUrl { get; set; }
/// &summary&
/// 消息id,64位整型
/// &/summary&
public string MsgId { get; set; }
/// &summary&
/// 媒体ID
/// &/summary&
public string MediaId { get; set; }
语音实体:
public class VoiceMessage : BaseMessage
/// &summary&
/// 缩略图ID
/// &/summary&
public string MsgId { get; set; }
/// &summary&
/// &/summary&
public string Format { get; set; }
/// &summary&
/// 媒体ID
/// &/summary&
public string MediaId { get; set; }
/// &summary&
/// 语音识别结果
/// &/summary&
public string Recognition { get; set; }
视频实体:
public class VideoMessage : BaseMessage
/// &summary&
/// 缩略图ID
/// &/summary&
public string ThumbMediaId { get; set; }
/// &summary&
/// 消息id,64位整型
/// &/summary&
public string MsgId { get; set; }
/// &summary&
/// 媒体ID
/// &/summary&
public string MediaId { get; set; }
链接实体:
public class LinkMessage : BaseMessage
/// &summary&
/// 缩略图ID
/// &/summary&
public string MsgId { get; set; }
/// &summary&
/// &/summary&
public string Title { get; set; }
/// &summary&
/// &/summary&
public string Description { get; set; }
/// &summary&
/// 链接地址
/// &/summary&
public string Url { get; set; }
消息实体定义好了,下一步就是根据微信服务器推送的消息体解析成对应的实体。本打算用C#自带的xml序列化发序列化的组件,结果试了下总是报什么xmls的错,索性用反射写了个处理方法:
public static T ConvertObj&T&(string xmlstr)
XElement xdoc = XElement.Parse(xmlstr);
var type = typeof(T);
var t = Activator.CreateInstance&T&();
foreach (XElement element in xdoc.Elements())
var pr = type.GetProperty(element.Name.ToString());
if (element.HasElements)
{//这里主要是兼容微信新添加的菜单类型。nnd,竟然有子属性,所以这里就做了个子属性的处理
foreach (var ele in element.Elements())
pr = type.GetProperty(ele.Name.ToString());
pr.SetValue(t, Convert.ChangeType(ele.Value, pr.PropertyType), null);
if (pr.PropertyType.Name == "MsgType")//获取消息模型
pr.SetValue(t, (MsgType)Enum.Parse(typeof(MsgType), element.Value.ToUpper()), null);
if (pr.PropertyType.Name == "Event")//获取事件类型。
pr.SetValue(t, (Event)Enum.Parse(typeof(Event), element.Value.ToUpper()), null);
pr.SetValue(t, Convert.ChangeType(element.Value, pr.PropertyType), null);
处理xml的方法定义好后,下面就是讲根据不同的消息类型来解析对应的实体了:
public class MessageFactory
public static BaseMessage CreateMessage(string xml)
XElement xdoc = XElement.Parse(xml);
var msgtype = xdoc.Element("MsgType").Value.ToUpper();
MsgType type = (MsgType)Enum.Parse(typeof(MsgType), msgtype);
switch (type)
case MsgType.TEXT: return Utils.ConvertObj&TextMessage&(xml);
case MsgType.IMAGE: return Utils.ConvertObj&ImgMessage&(xml);
case MsgType.VIDEO: return Utils.ConvertObj&VideoMessage&(xml);
case MsgType.VOICE: return Utils.ConvertObj&VoiceMessage&(xml);
case MsgType.LINK:
return Utils.ConvertObj&LinkMessage&(xml);
case MsgType.LOCATION:
return Utils.ConvertObj&LocationMessage&(xml);
case MsgType.EVENT://事件类型
return Utils.ConvertObj&BaseMessage&(xml);
CreateMessage方法传入数据包(如加密,需解密后传入),以基类的形式返回对应的实体。
讲到这里普通消息的接收就差不多讲完了,结合上一篇博文,现在把修改后的接入代码贴出来如下:
public class WxRequest
public static BaseMessage Load(EnterParam param, bool bug = true)
string postStr = "";
Stream s = VqiRequest.GetInputStream();//此方法是对System.Web.HttpContext.Current.Request.InputStream的封装,可直接代码
byte[] b = new byte[s.Length];
s.Read(b, 0, (int)s.Length);
postStr = Encoding.UTF8.GetString(b);//获取微信服务器推送过来的字符串
var timestamp = VqiRequest.GetQueryString("timestamp");
var nonce = VqiRequest.GetQueryString("nonce");
var msg_signature = VqiRequest.GetQueryString("msg_signature");
var encrypt_type = VqiRequest.GetQueryString("encrypt_type");
string data = "";
if (encrypt_type=="aes")//加密模式处理
param.IsAes = true;
var ret = new MsgCrypt(param.token, param.EncodingAESKey, param.appid);
int r = ret.DecryptMsg(msg_signature, timestamp, nonce, postStr, ref data);
if (r != 0)
WxApi.Base.WriteBug("消息解密失败");
return null;
param.IsAes = false;
data = postS
Utils.WriteTxt(data);
return MessageFactory.CreateMessage(data);
打完收工&&,晚安。
时间仓促,如有不明白的,请留言,如果你觉得本篇博文对你有帮助,请点击一下推荐,推荐给更多的朋友的。
各位有建议或者意见可留言给我哦,或者加如QQ群一起进行交流。
如果你是土豪,可以扫描下面的二维码悬赏一下,你的支持是笔者继续更新下去的动力。
阅读(...) 评论()}

我要回帖

更多关于 微信消息重复提醒 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信