• 微信公众号的开发 Senparc.Weixin.dll使用


       项目需要,做个微信公众号,之前从未做过,前期挺懵的,再次记录一下,一切困难都是纸老虎(哈哈)

    服务号是公司申请的微信公共账号,订阅号是个人申请的。建议开发者自己申请一个测试账号,方便使用,但是测试账号不能测试使用支付功能,如果牵扯到支付的功能,建议先用测试账号把其他的做好,然后使用正式的账号,测试支付的功能(个人思路哈)

    好了,接下来是项目了(以MVC项目为例):

    引用的dll

    测试账号的申请

    地址:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login

    需要填写Url和token:注意这里的Url是有限制的,需要讲默的页面配置为Url!

    接下来便是代码了:

    首先你需要建立一个CustomMessageHandler类,继承自dll中的类,如下:

     
        ///
        /// 微信消息处理
        /// 
        ///  根据微信原始Id, 判断具体推送给哪一个用户
        /// 
        ///
        public partial class CustomMessageHandler : MessageHandler<MessageContext<Senparc.Weixin.MP.Entities.IRequestMessageBase, Senparc.Weixin.MP.Entities.IResponseMessageBase>>
        {
          
    
            private static string AppID = ConfigManager.Config.Wechat.AppID;
    
            static object _locker = new object();
            public CustomMessageHandler(Stream inputStream, PostModel postModel = null, int maxRecordCount = 0) : base(inputStream, postModel, maxRecordCount)
            {
              
                base.CurrentMessageContext.ExpireMinutes = 10;
            }
    
            public CustomMessageHandler(XDocument requestDocument, PostModel postModel = null, int maxRecordCount = 0) : base(requestDocument, postModel, maxRecordCount)
            {
            }
    
            public CustomMessageHandler(RequestMessageBase requestMessageBase, PostModel postModel = null, int maxRecordCount = 0) : base(requestMessageBase, postModel, maxRecordCount)
            {
            }
    
            #region 用户关注公众号
            ///
            /// 用户关注公众号
            ///
            ///
            ///
            public async override Task OnEvent_SubscribeRequestAsync(RequestMessageEvent_Subscribe requestMessage)
            {
                try
                {
                    if (AppID != null)
                    {
                        var userInfo = await UserApi.InfoAsync(AppID, base.WeixinOpenId);
                        if (userInfo == null)
                        {
                            Sprite.Agile.Logger.LoggerManager.Instance.Logger_Info($"获取用户信息失败");
                        }
                        else
                        {
                            Sprite.Agile.Logger.LoggerManager.Instance.Logger_Info($"获取用户信息,OpenId:{userInfo.openid}, WeixinOpenId:{WeixinOpenId}");
                        }
    
                        #region 处理用户信息
                        if (requestMessage.Event == Event.subscribe)
                        {
                            var user = usersRepository.GetUserByOpenId(userInfo.openid);
                            Sprite.Agile.Logger.LoggerManager.Instance.Logger_Info($"二维码场景值{userInfo.qr_scene}");
                            if (user == null)
                            {
                                user = new 你的用户类
                                {
                                    OpenId = userInfo.openid,
                                    WeChatNickName = userInfo.nickname,
                                    HeadImg = userInfo.headimgurl,
                                    Status = Sprite.Agile.Domain.Status.Disabled,
                                    UserType=Sprite.Agile.Model.Domain.Entities.UserType.User,
                                    IsSubscribe = true
                                };
                                //判断是否通过扫描进入关注
                                if (userInfo.subscribe_scene == "ADD_SCENE_QR_CODE")
                                {
                                    //暂时通过场景Id识别上级
                                    var parent = usersRepository.GetUserByScanId(userInfo.qr_scene);
                                    if(parent!=null)
                                    {
                                        user.ParentId = parent.Id;
                                    }
                                }
    
                                usersRepository.AddUser(user);
                            }
                            else
                            {
                                user.OpenId = userInfo.openid;
                                user.HeadImg = userInfo.headimgurl;
                                user.WeChatNickName = userInfo.nickname;
                                user.IsSubscribe = true;
                               
                                usersRepository.SaveUser(user);
                            }
                        }
                        #endregion
    
                        var responseMessage = requestMessage.CreateResponseMessage();
                        responseMessage.Content = $"  感谢您的关注";
                        return responseMessage;
                    }
                    else
                    {
                        Sprite.Agile.Logger.LoggerManager.Instance.Logger_Info($"用户关注事件, 微信商户信息为空:{requestMessage.ToUserName}");
                        return new ResponseMessageNoResponse();
                    }
                }
                catch (Exception ex)
                {
                    Sprite.Agile.Logger.LoggerManager.Instance.Logger_Error(ex);
                    return new ResponseMessageNoResponse();
                }
    
            }
            #endregion
    
            #region 用户取消关注
            ///
            /// 用户取消关注
            ///
            ///
            ///
            public async override Task OnEvent_UnsubscribeRequestAsync(RequestMessageEvent_Unsubscribe requestMessage)
            {
                try
                {
                    var wechat = new UserBaseController();
                    if (wechat != null)
                    {
                        var userInfo = await UserApi.InfoAsync(wechat.AppId, base.WeixinOpenId);
    
                        #region 处理用户信息
                        if (requestMessage.Event == Event.unsubscribe)
                        {
                            var user = usersRepository.GetUserByOpenId(userInfo.openid);
                            if (user != null)
                            {
    
                                user.IsSubscribe = false;
                                usersRepository.SaveUser(user);
                            }
                        }
                        #endregion
                    }
                    else
                    {
                        Sprite.Agile.Logger.LoggerManager.Instance.Logger_Info($"用户取消关注事件, 微信商户信息为空:{requestMessage.ToUserName}");
                    }
    
                    return new ResponseMessageNoResponse();
                }
                catch (Exception ex)
                {
                    Sprite.Agile.Logger.LoggerManager.Instance.Logger_Error(ex);
                    return new ResponseMessageNoResponse();
                }
            }
            #endregion      
    
            #region 处理文字请求
            ///
            /// 处理文字请求
            ///
            ///
            ///
            public async override Task OnTextRequestAsync(RequestMessageText requestMessage)
            {
                Sprite.Agile.Logger.LoggerManager.Instance.Logger_Info($"处理文字请求,ToUserName:{requestMessage.ToUserName}, WeixinOpenId:{WeixinOpenId}");
                try
                {
                    return await Task.Factory.StartNew(() =>
                    {
                        var responseMessage = requestMessage.CreateResponseMessage();
                        //responseMessage.Content = $"文本消息,ToUserName:{requestMessage.ToUserName}, WeixinOpenId:{WeixinOpenId}";
                        responseMessage.Content = $"欢迎*****微信公众号";
                        return responseMessage;
                    }
                    );
                }
                catch (Exception)
                {
    
                    return new ResponseMessageNoResponse();
                };
            }
            #endregion
    
            public async override Task OnEvent_ScanRequestAsync(RequestMessageEvent_Scan requestMessage)
            {
                //通过扫描关注
                var responseMessage = CreateResponseMessage();
    
                responseMessage.Content = $"  您已经是我们的用户啦!" ; 
                return responseMessage;
            }
    
    
            #region 默认触发事件
            ///
            /// 默认触发事件
            ///
            ///
            ///
            public async override Task DefaultResponseMessageAsync(IRequestMessageBase requestMessage)
            {
                try
                {
                    return await Task.Factory.StartNew(() =>
                    {
                        var responseMessage = requestMessage.CreateResponseMessage();
                        responseMessage.Content = $"默认,ToUserName:{requestMessage.ToUserName}, WeixinOpenId:{WeixinOpenId}";
                        return responseMessage;
                    }
                    );
                }
                catch (Exception)
                {
    
                    return new ResponseMessageNoResponse();
                };
            }
    
            ///
            /// 默认触发事件
            ///
            ///
            /// 
            public override IResponseMessageBase DefaultResponseMessage(IRequestMessageBase requestMessage)
            {
                try
                {
                    var responseMessage = requestMessage.CreateResponseMessage();
                    responseMessage.Content = "你需要推送的内容";
                    return responseMessage;
    
                }
                catch (Exception)
                {
    
                    return new ResponseMessageNoResponse();
                };
            }
            #endregion
        }
    
    
    

    然后,你需要建立一个基础的Controller,来进行拦截判断权限

        ///
        /// 微信认证模块
        ///
        [AllowAnonymous]
        public class BaseController : Controller
        { 
            //这些文件一般配置zai webconfig中
            private static string Token = ConfigManager.Config.Wechat.Token;
            private static string AppID = ConfigManager.Config.Wechat.AppID;
            private static string AppSecret = ConfigManager.Config.Wechat.AppSecret;
            private static string EncodingAESKey = ConfigManager.Config.Wechat.EncodingAESKey;
    
            IYueSaoUserService userServices;
    
            public BaseController()
            {
                userServices = Sprite.Agile.Plugins.PluginManager.Resolve();
            }
    
            #region  
    
            // 生成随机文件名
            readonly Func _getRandomFileName = () => DateTime.Now.ToString("yyyyMmdd-HHmmss") + Guid.NewGuid().ToString("n").Substring(0, 6);
    
    
            [HttpGet]
            [ActionName("Index")]
            public Task Get(string signature, string timestamp, string nonce, string echostr)
            {
                return Task.Factory.StartNew(() =>
                {
                    if (CheckSignature.Check(signature, timestamp, nonce, Token))
                    {
                        return echostr; //返回随机字符串则表示验证通过
                    }
                    else
                    {
                        return "failed:" + signature + "," + CheckSignature.GetSignature(timestamp, nonce, Token) + "。" +
                            "如果你在浏览器中看到这句话,说明此地址可以被作为微信公众账号后台的Url,请注意保持Token一致。";
                    }
                }).ContinueWith(task => Content(task.Result));
            }
    
            ///
            /// 用户发送消息后,微信平台自动Post一个请求到这里,并等待响应XML
            ///
            [HttpPost]
            [ActionName("Index")]
            public async Task Post(PostModel postModel)
            {
                if (!CheckSignature.Check(postModel.Signature, postModel.Timestamp, postModel.Nonce, Token))
                {
                    return new WeixinResult("参数错误!");
                }
    
    
    
                #region 打包 PostModel 信息
    
                postModel.Token = Token;
                postModel.EncodingAESKey = EncodingAESKey; //根据自己后台的设置保持一致
                postModel.AppId = AppID; //根据自己后台的设置保持一致
    
                #endregion
                var messageHandler = new CustomMessageHandler(Request.InputStream, postModel, 10);
    
                #region 设置消息去重
    
                /* 如果需要添加消息去重功能,只需打开OmitRepeatedMessage功能,SDK会自动处理。
                 * 收到重复消息通常是因为微信服务器没有及时收到响应,会持续发送2-5条不等的相同内容的RequestMessage*/
                messageHandler.OmitRepeatedMessage = true;//默认已经开启,此处仅作为演示,也可以设置为false在本次请求中停用此功能
    
                #endregion
    
                try
                {
                    #region 记录 Request 日志
    
                    var logPath = Server.MapPath(string.Format("~/App_Data/MP/{0}/", DateTime.Now.ToString("yyyy-MM-dd")));
                    if (!Directory.Exists(logPath))
                    {
                        Directory.CreateDirectory(logPath);
                    }
    
                    //测试时可开启此记录,帮助跟踪数据,使用前请确保App_Data文件夹存在,且有读写权限。
                    messageHandler.RequestDocument.Save(Path.Combine(logPath, string.Format("{0}_Request_{1}_{2}.txt", _getRandomFileName(),
                        messageHandler.RequestMessage.FromUserName,
                        messageHandler.RequestMessage.MsgType)));
                    if (messageHandler.UsingEcryptMessage)
                    {
                        messageHandler.EcryptRequestDocument.Save(Path.Combine(logPath, string.Format("{0}_Request_Ecrypt_{1}_{2}.txt", _getRandomFileName(),
                            messageHandler.RequestMessage.FromUserName,
                            messageHandler.RequestMessage.MsgType)));
                    }
    
                    #endregion
                    await messageHandler.ExecuteAsync(); //执行微信处理过程
                    //messageHandler.Execute(); //执行微信处理过程
    
                    #region 记录 Response 日志
    
                    //测试时可开启,帮助跟踪数据
    
                    //if (messageHandler.ResponseDocument == null)
                    //{
                    //    throw new Exception(messageHandler.RequestDocument.ToString());
                    //}
                    if (messageHandler.ResponseDocument != null)
                    {
                        messageHandler.ResponseDocument.Save(Path.Combine(logPath, string.Format("{0}_Response_{1}_{2}.txt", _getRandomFileName(),
                            messageHandler.ResponseMessage.ToUserName,
                            messageHandler.ResponseMessage.MsgType)));
                    }
    
                    if (messageHandler.UsingEcryptMessage && messageHandler.FinalResponseDocument != null)
                    {
                        //记录加密后的响应信息
                        messageHandler.FinalResponseDocument.Save(Path.Combine(logPath, string.Format("{0}_Response_Final_{1}_{2}.txt", _getRandomFileName(),
                            messageHandler.ResponseMessage.ToUserName,
                            messageHandler.ResponseMessage.MsgType)));
                    }
    
                    #endregion
    
                    //return Content(messageHandler.ResponseDocument.ToString());//v0.7-
                    return new WeixinResult(messageHandler);//v0.8+
                                                            //return new FixWeixinBugWeixinResult(messageHandler);//为了解决官方微信5.0软件换行bug暂时添加的方法,平时用下面一个方法即可
    
                }
                catch (Exception ex)
                {
                    #region 异常处理
                    //WeixinTrace.Log("MessageHandler错误:{0}", ex.Message);
    
                    using (TextWriter tw = new StreamWriter(Server.MapPath("~/App_Data/Error_" + _getRandomFileName() + ".txt")))
                    {
                        tw.WriteLine("ExecptionMessage:" + ex.Message);
                        tw.WriteLine(ex.Source);
                        tw.WriteLine(ex.StackTrace);
                        //tw.WriteLine("InnerExecptionMessage:" + ex.InnerException.Message);
    
                        if (messageHandler.ResponseDocument != null)
                        {
                            tw.WriteLine(messageHandler.ResponseDocument.ToString());
                        }
    
                        if (ex.InnerException != null)
                        {
                            tw.WriteLine("========= InnerException =========");
                            tw.WriteLine(ex.InnerException.Message);
                            tw.WriteLine(ex.InnerException.Source);
                            tw.WriteLine(ex.InnerException.StackTrace);
                        }
    
                        tw.Flush();
                        tw.Close();
                    }
                    return Content("");
                    #endregion
                }
    
    
            }
    
            #endregion 
    
            #region 微信授权
            ///
            /// 微信授权登录页面
            ///
            ///跳转地址
            ///
            public async Task Login(string returnUrl)
            {
                var appId = AppID;
    
                return await Task.Factory.StartNew(() =>
                {
                    var doMain = Request.Url.Scheme + "://" + Request.Url.Host;
                    var redirect_uri = $"{doMain}{Url.Action("Callback", new { returnUrl = returnUrl })}";
                    var state = "FenXiao-" + DateTime.Now.Millisecond;
                    Session["State"] = state;
    
                    #region 通过参数信息,拿到用户访问的appid
    
                    #endregion
    
                    var oauthUrl = OAuthApi.GetAuthorizeUrl(appId, redirect_uri, state, OAuthScope.snsapi_userinfo);
    
                    return oauthUrl;
                }).ContinueWith(task =>
                {
                    var url = task.Result;
                    return Redirect(task.Result);
                });
            }
    
            ///
            /// 微信回调
            ///
            ///
            ///
            ///
            ///
            public ActionResult Callback(string code, string state, string returnUrl)
            {
                Sprite.Agile.Logger.LoggerManager.Instance.Logger_Info($"微信回调{returnUrl}");
                var url = string.Empty;
                var doMain = Request.Url.Scheme + "://" + Request.Url.Host;
    
                if (string.IsNullOrEmpty(code))
                {
                    url = Url.Action("Msg", "Msg", new { msg = "您拒绝了授权!" });
                    return Redirect($"{doMain}/{url}");
                }
    
                if (state != Session["State"] as string)
                {
                    Session["State"] = null;
                    url = Url.Action("Msg", "Msg", new { msg = "验证失败!请从正规途径进入!" });
                    return Redirect($"{doMain}/{url}");
                }
    
                OAuthAccessTokenResult result = null;
                try
                {
                    result = OAuthApi.GetAccessToken(AppID, AppSecret, code);
                }
                catch (Exception ex)
                {
                    url = Url.Action("Msg", "Msg", new { msg = ex.Message });
                    return Redirect($"{doMain}/{url}");
                }
    
                if (result.errcode != Senparc.Weixin.ReturnCode.请求成功)
                {
                    url = Url.Action("Msg", "Msg", new { msg = result.errmsg });
                    return Redirect($"{doMain}/{url}");
                }
    
                Session["State"] = null;
    
                //下面2个数据也可以自己封装成一个类,储存在数据库中(建议结合缓存)
                //如果可以确保安全,可以将access_token存入用户的cookie中,每一个人的access_token是不一样的
                Session["OAuthAccessTokenStartTime"] = DateTime.Now;
                Session["OAuthAccessToken"] = result;
    
                var oauthAccessToken = result.access_token;
                var openId = result.openid;
                var userInfo = OAuthApi.GetUserInfo(oauthAccessToken, openId);
    
                Session["OpenId"] = openId;
                //Session["AppId"] = appId;
    
                #region 查找数据库看用户信息是不是存在, 根据用户信息存在与否执行不同操作
                try
                {
    
                    var user = userServices.GetUserByOpenId(openId);
    
                    if (user == null)
                    {
                        user = new Sprite.Agile.Model.Domain.Entities.YueSaoUserManage.YueSaoUser
                        {
                            OpenId = openId,
                            WeChatNickName = userInfo.nickname,
                            HeadImg = userInfo.headimgurl,
                            UserType = Sprite.Agile.Model.Domain.Entities.UserType.User,
                            Status = Sprite.Agile.Domain.Status.Disabled,
                        };
                        userServices.AddUser(user);
                    }
                    else
                    {
    
                        #region 跟新用户头像 & 昵称
    
                        user.WeChatNickName = userInfo.nickname;
                        user.HeadImg = userInfo.headimgurl;
                        userServices.SaveUser(user);
    
                        #endregion
                    }
    
                    Session["UserId"] = user.Id;
                    if (user.ParentId != null)
                        Session["ParentId"] = user.ParentId;
                    Sprite.Agile.Logger.LoggerManager.Instance.Logger_Info($"old:{user.Id}   new:{ Session["UserId"]}");
                    Session["HeadImg"] = user.HeadImg;
                    Session["WeChatNickName"] = user.WeChatNickName;
                }
                catch (Exception ex)
                {
                    Sprite.Agile.Logger.LoggerManager.Instance.Logger_Info($"回调错误信息{ex.Message}");
                    url = Url.Action("Msg", "Msg", new { msg = "从DB中获取用户信息失败" });
                    return Redirect($"{doMain}/{url}");
                }
                #endregion
    
                if (returnUrl.IndexOf('?') != -1)
                {
                    returnUrl += "&appId=" + AppID;
                }
                else
                {
                    returnUrl += "?appId=" + AppID;
                }
                return Redirect(returnUrl);
    
            }
    
            #endregion
    
            #region  菜单配置
    
            ///
            /// 微信创建菜单
            ///
            ///
            /// 
            [HttpGet]
            public async Task CreateMenu(string keyValue)
            {
                var data = "成功";
                try
                {
                    if (keyValue.IsEmputy())
                    {
                        data = "参数错误";
                    }
                    else if (AppID != null)
                    {
                        var accessToken = await AccessTokenContainer.GetAccessTokenAsync(AppID);
                        var wechatWeb = 你的Url;// $"{Request.Url.Scheme}://{Request.Url.Host}";
                        Sprite.Agile.Logger.LoggerManager.Instance.Logger_Debug($"菜单地址:{wechatWeb}");
                        ButtonGroup bg = new ButtonGroup();
    
                        bg.button.Add(new SingleViewButton()
                        {
                            url = $"{wechatWeb}/Home/Index?appId={AppID}",
                            name = "首页"
                        });
    
    
                        bg.button.Add(new SingleViewButton()
                        {
                            url = $"{wechatWeb}/TrainingRegistration/NannyTrain?appId={AppID}",
                            name = "*****"
                        });
    
                        bg.button.Add(new SingleViewButton()
                        {
                            url = $"{wechatWeb}/YueSaoUser/PersonalCenters?appId={AppID}",
                            name = "个人中心"
                        });
    
                        var result = CommonApi.CreateMenu(accessToken, bg);
                        if (result.errcode == ReturnCode.请求成功)
                        {
                            data = "菜单同步成功";
                        }
                        else
                        {
                            data = "菜单同步失败";
                        }
                    }
                }
                catch (Exception e)
                {
                    data = e.Message;
                }
                ViewBag.Message = data;
                return View();
            }
    
            #endregion 
    
    
    
        }
    
    
    
    
    

    注意上面的菜单配置,需要调用一下接口才会有效果的!

  • 相关阅读:
    在Linux(Ubuntu)下安装Arial、Times New Roman等字体
    Qt的安装和使用中的常见问题(简略版)
    Qt的安装和使用中的常见问题(详细版)
    机械+固态双硬盘时机械硬盘卡顿问题解决
    在Qt(C++)中与Python混合编程
    在Notepad++中快捷选中多行
    在Linux下访问Windows共享文件夹
    Ubuntu下U盘只读文件系统,图标上锁,提示无法修改
    使用Qt Installer Framework制作软件安装包
    Swoole练习 websocket
  • 原文地址:https://www.cnblogs.com/kekelele/p/9989174.html
Copyright © 2020-2023  润新知