一、说明
在微信用户和公众号产生交互的过程中,用户的某些操作会使得微信服务器通过事件推送的形式通知到开发者在开发者中心处设置的服务器地址,从而开发者可以获取到该信息。
二、新建partial 类
partial即为定义部分类的关键字。部分类主要用于当一个类中的内容较多时将相似类中的内容拆分到不同的类中,并且部分类的名称必须相同。
CustomMessageHandler.cs加关键词partial
Handler下新建CustomMessageHandler_Events部分类用来接收事件推送
修改类名为CustomMessageHandler并添加关键词partial使得CustomMessageHandler_Events为CustomMessageHandler的部分类,这样推送事件和普通消息事件就分开了,职责清晰,便于维护。
namespace WeiXinApi.Application.Handler
{
public partial class CustomMessageHandler
{
}
}
同样的我们新建MessageService_Events部分类
namespace WeiXinApi.Application.Services
{
public partial interface IMessageService
{
}
}
namespace WeiXinApi.Application.Services
{
public partial class MessageService
{
}
}
IMessageService和IMessageService也要加partial关键字
三、关注/取消关注事件
IMessageService_Events定义接口
/// <summary>
/// 订阅
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
Task<IResponseMessageBase> OnEvent_SubscribeRequestAsync(RequestMessageEvent_Subscribe requestMessage);
/// <summary>
/// 取消订阅
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
Task<IResponseMessageBase> OnEvent_UnsubscribeRequest(RequestMessageEvent_Unsubscribe requestMessage);
MessageService_Events实现接口
public async Task<IResponseMessageBase> OnEvent_SubscribeRequestAsync(RequestMessageEvent_Subscribe requestMessage)
{
var responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
var receives = await DbContext.Db.Queryable<MessageReceive>().ToListAsync();
//如果关键字搜不到,列出关键字
var result = new StringBuilder();
result.AppendFormat("欢迎订阅,可以试试下面的关键字\r\n\r\n");
for (int i = 0; i < receives.Count; i++)
{
result.AppendFormat($"{i + 1}:{receives[i].KeyWords}\r\n");
}
responseMessage.Content = result.ToString();
return responseMessage;
}
/// <summary>
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public async Task<IResponseMessageBase> OnEvent_UnsubscribeRequest(RequestMessageEvent_Unsubscribe requestMessage)
{
//实际上用户无法收到非订阅账号的消息,所以这里可以随便写。
var responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
responseMessage.Content = $"再见!";
Console.WriteLine($"{requestMessage.FromUserName}取消了订阅");
return responseMessage;
}
CustomMessageHandler_Events里重写订阅和取消订阅事件
/// <summary>
/// 订阅或关注事件
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override async Task<IResponseMessageBase> OnEvent_SubscribeRequestAsync(RequestMessageEvent_Subscribe requestMessage)
{
var result = await _messageService.OnEvent_SubscribeRequestAsync(requestMessage);
return result;
}
/// <summary>
/// 退订
/// unsubscribe事件的意义在于及时删除网站应用中已经记录的OpenID绑定,消除冗余数据。并且关注用户流失的情况。
/// </summary>
/// <returns></returns>
public override async Task<IResponseMessageBase> OnEvent_UnsubscribeRequestAsync(RequestMessageEvent_Unsubscribe requestMessage)
{
var result = await _messageService.OnEvent_UnsubscribeRequest(requestMessage);
return result;
}
发布到服务器,测试下效果,成功接收到事件
四、扫描带参数二维码事件
IMessageService_Events定义接口
/// <summary>
/// 扫描带参数二维码事件
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
Task<IResponseMessageBase> OnEvent_ScanRequestAsync(RequestMessageEvent_Scan requestMessage);
MessageService_Events实现接口
public async Task<IResponseMessageBase> OnEvent_ScanRequestAsync(RequestMessageEvent_Scan requestMessage)
{
var responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
var result = "扫描带二维码事件";
var sceneId = requestMessage.EventKey.ObjToInt();
var receive = await DbContext.Db.Queryable<QrCode>().Where(it => it.SceneId == sceneId).FirstAsync();//从数据库拿数据
Console.WriteLine($"是否有数据:{receive != null}");
if (receive != null)
{
switch (receive.ReceiveInfo.ReceiveType)
{
case ReceiveType.文字:
result += $"{receive.ReceiveInfo.ReceiveString}";
break;
}
}
responseMessage.Content = result;
return responseMessage;
}
CustomMessageHandler_Events重写扫描二维码事件
/// <summary>
/// 扫描带参数二维码事件
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override async Task<IResponseMessageBase> OnEvent_ScanRequestAsync(RequestMessageEvent_Scan requestMessage)
{
var result = await _messageService.OnEvent_ScanRequestAsync(requestMessage);
return result;
}
先用api接口创建一个二维码,然后发布到服务器,记得sqlite文件为始终复制,可以正常显示
先取消关注再扫码,发现只回复了“扫描带二维码事件”这几个字,发现原来在没有关注的情况下会将eventkey推送到订阅事件
因为关注事件和扫码二维码事件都会去数据库查数据,所以将共用的方法提取出来,编写方法GetScanRequest
/// <summary>
/// 获取扫描事件结果
/// </summary>
/// <param name="eventKey"></param>
/// <returns></returns>
private async Task<string> GetScanRequest(string eventKey)
{
Console.WriteLine($"eventKey:{eventKey}");
var result = "扫描带二维码事件!";
var sceneId = eventKey.ObjToInt();
var receive = await DbContext.Db.Queryable<QrCode>().Where(it => it.SceneId == sceneId).FirstAsync();//从数据库拿数据
Console.WriteLine($"是否有数据:{receive != null}");
if (receive != null)
{
switch (receive.ReceiveInfo.ReceiveType)
{
case ReceiveType.文字:
result += $"{receive.ReceiveInfo.ReceiveString}";
break;
}
}
return result;
}
修改OnEvent_SubscribeRequestAsync方法
public async Task<IResponseMessageBase> OnEvent_SubscribeRequestAsync(RequestMessageEvent_Subscribe requestMessage)
{
var responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
if (!string.IsNullOrWhiteSpace(requestMessage.EventKey) && requestMessage.EventKey.StartsWith("qrscene_"))//扫二维码事件
{
responseMessage.Content = await GetScanRequest(requestMessage.EventKey.Split("_")[1]);
}
else
{
var receives = await DbContext.Db.Queryable<MessageReceive>().ToListAsync();
//如果关键字搜不到,列出关键字
var result = new StringBuilder();
result.AppendFormat("欢迎订阅,可以试试下面的关键字\r\n\r\n");
for (int i = 0; i < receives.Count; i++)
{
result.AppendFormat($"{i + 1}:{receives[i].KeyWords}\r\n");
}
responseMessage.Content = result.ToString();
}
return responseMessage;
}
OnEvent_ScanRequestAsync也改下
public async Task<IResponseMessageBase> OnEvent_ScanRequestAsync(RequestMessageEvent_Scan requestMessage)
{
var responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
responseMessage.Content = await GetScanRequest(requestMessage.EventKey);
return responseMessage;
}
发布到服务器,取消关注再扫描,成功显示数据
五、上报地理位置事件
IMessageService_Events定义接口
/// <summary>
/// 位置事件
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
Task<IResponseMessageBase> OnEvent_LocationRequestAsync(RequestMessageEvent_Location requestMessage);
MessageService_Events实现接口
public async Task<IResponseMessageBase> OnEvent_LocationRequestAsync(RequestMessageEvent_Location requestMessage)
{
//这里是微信客户端(通过微信服务器)自动发送过来的位置信息
var responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
var result = $"Latitude:{requestMessage.Latitude},Longitude:{requestMessage.Longitude}";
Console.WriteLine(result);
responseMessage.Content = result;//这里写什么都无所谓
return responseMessage;
}
CustomMessageHandler_Events重写位置事件
/// <summary>
/// 位置事件
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override async Task<IResponseMessageBase> OnEvent_LocationRequestAsync(RequestMessageEvent_Location requestMessage)
{
var result = await _messageService.OnEvent_LocationRequestAsync(requestMessage);
return result;
}
开通位置权限
测试一下,正常打印日志
六、自定义菜单事件
6.1、创建菜单
参考自定义菜单章节
6.2、点击事件
IMessageService_Events定义接口
/// <summary>
/// 点击事件
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
Task<IResponseMessageBase> OnEvent_ClickRequestAsync(RequestMessageEvent_Click requestMessage);
MessageService_Events实现接口
public async Task<IResponseMessageBase> OnEvent_ClickRequestAsync(RequestMessageEvent_Click requestMessage)
{
Console.WriteLine("进入点击事件");
IResponseMessageBase reponseMessage = null;
switch (requestMessage.EventKey)
{
case "V1001_TODAY_MUSIC":
{
var filePath = "~/wwwroot/Images/music.jpeg";//记得调成始终复制
var uploadResult = await MediaApi.UploadTemporaryMediaAsync(appId, UploadMediaFileType.thumb,
ServerUtility.ContentRootMapPath(filePath));
//PS:缩略图官方没有特别提示文件大小限制,实际测试哪怕114K也会返回文件过大的错误,因此尽量控制在小一点
//设置音乐信息
var strongResponseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageMusic>(requestMessage);
reponseMessage = strongResponseMessage;
strongResponseMessage.Music.Title = "天籁之音";
strongResponseMessage.Music.Description = "真的是天籁之音";
strongResponseMessage.Music.MusicUrl = "https://sdk.weixin.senparc.com/Content/music1.mp3";
strongResponseMessage.Music.HQMusicUrl = "https://sdk.weixin.senparc.com/Content/music1.mp3";
strongResponseMessage.Music.ThumbMediaId = uploadResult.thumb_media_id;
}
break;
default:
{
var strongResponseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
strongResponseMessage.Content = "您点击了按钮,EventKey:" + requestMessage.EventKey;
reponseMessage = strongResponseMessage;
}
break;
}
return reponseMessage;
}
CustomMessageHandler_Events重写点击事件
/// <summary>
/// 点击事件
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override async Task<IResponseMessageBase> OnEvent_ClickRequestAsync(RequestMessageEvent_Click requestMessage)
{
var result = await _messageService.OnEvent_ClickRequestAsync(requestMessage);
return result;
}
发布到服务器,测试下,没毛病,这里缩略图不知道为啥没显示,不管了,后面再看看啥问题。
七、点击菜单跳转链接时的事件推送
IMessageService_Events定义接口
/// <summary>
/// 打开网页事件
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
Task<IResponseMessageBase> OnEvent_ViewRequestAsync(RequestMessageEvent_View requestMessage);
MessageService_Events实现接口
public async Task<IResponseMessageBase> OnEvent_ViewRequestAsync(RequestMessageEvent_View requestMessage)
{
Console.WriteLine("进入链接跳转事件");
//说明:这条消息只作为接收,下面的responseMessage到达不了客户端,类似OnEvent_UnsubscribeRequest
var responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
responseMessage.Content = "您点击了view按钮,将打开网页:" + requestMessage.EventKey;
return responseMessage;
}
CustomMessageHandler_Events重写打开网页事件
/// <summary>
/// 打开网页事件
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override async Task<IResponseMessageBase> OnEvent_ViewRequestAsync(RequestMessageEvent_View requestMessage)
{
var result = await _messageService.OnEvent_ViewRequestAsync(requestMessage);
return result;
}
发布到服务器,测试下,没毛病。
八、本章Gitee地址链接
weixinapi: .Net6+Fruion+Sqlsugar+SenparcSdk开发微信公众号Demo - Gitee.com