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

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

好了,接下来是项目了(以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 }

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

微信公众号的开发 Senparc.Weixin.dll使用的更多相关文章

  1. C#微信公众号接口开发,灵活利用网页授权、带参数二维码、模板消息,提升用户体验之完成用户绑定个人微信及验证码获取

    一.前言 当下微信公众号几乎已经是每个公司必备的,但是大部分微信公众账号用户体验都欠佳,特别是涉及到用户绑定等,需要用户进行复杂的操作才可以和网站绑定,或者很多公司直接不绑定,而是每次都让用户填写账号 ...

  2. C#/ASP.NET MVC微信公众号接口开发之从零开发(四) 微信自定义菜单(附源码)

    C#/ASP.NET MVC微信接口开发文章目录: 1.C#/ASP.NET MVC微信公众号接口开发之从零开发(一) 接入微信公众平台 2.C#/ASP.NET MVC微信公众号接口开发之从零开发( ...

  3. C#/ASP.NET MVC微信公众号接口开发之从零开发(二) 接收微信消息并且解析XML(附源码)

    文章导读: C#微信公众号接口开发之从零开发(一) 接入微信公众平台 微信接入之后,微信通过我们接入的地址进行通信,其中的原理是微信用户发送消息给微信公众账号,微信服务器将消息以xml的形式发送到我们 ...

  4. C#/ASP.NET MVC微信公众号接口开发之从零开发(三)回复消息 (附源码)

    C#/ASP.NET MVC微信接口开发文章目录: 1.C#/ASP.NET MVC微信公众号接口开发之从零开发(一) 接入微信公众平台 2.C#/ASP.NET MVC微信公众号接口开发之从零开发( ...

  5. 微信公众号支付开发全过程 --JAVA

    按照惯例,开头总得写点感想 ------------------------------------------------------------------ 业务流程 这个微信官网说的还是很详细的 ...

  6. 到处是坑的微信公众号支付开发(java)

    之前公司项目开发中支付是用阿里的支付做的,那叫一个简单,随意:悲催的是,现在公司开发了微信公众号,所以我步入了全是坑的微信支付开发中... ------------------------------ ...

  7. Senparc.Weixin SDK 微信公众号 .NET 开发教程 索引

    Senparc.WeixinSDK从一开始就坚持开源的状态,这个过程中得到了许多朋友的认可和支持. 目前SDK已经达到比较稳定的版本,这个过程中我觉得有必要整理一些思路和经验,和大家一起分享.也欢迎大 ...

  8. 微信JS-SDK]微信公众号JS开发之卡券领取功能详解

    js sdk: http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD.952-.E6 ...

  9. PHP微信公众号后台开发(Yii2实现)

    本文内容较多,包括微信接入.获取微信用户信息.微信支付.JSSDK配置参数获取等部分.如果读者对微信开发没有一个主观上的认识,那么建议读者先研读微信公众平台开发者文档,然后再阅读本文,效果更佳!另外本 ...

随机推荐

  1. Spring 开发常见问题

    linux 下http 接收中文参数乱码 解决: 在application.yml配置文件中添加 spring: http: encoding: charset: GB2312

  2. About The Order of The Declarations And Definition When Making a Member Function a Friend.关于使类成员成为另一个类友元函数的声明顺序和定义。

    If only member function clear of WindowMgr is a friend of Screen, there are some points need to note ...

  3. java文件过滤器的使用

    前言: java.io.FileFilter(过滤器接口)boolean accept(File pathname) File类提供了如下方法使用过滤器:public File[] listFiles ...

  4. IntelliJ IDEA 2018.3.3配置 Tomcat 9,控制台出现中文乱码 “淇℃伅”(2019/01/25)

    (win10系统) 全新idea配置全新版本Tomcat突遇 “淇℃伅”,网上大部分解决方案均已失效 似乎是idea与Tomcat命令行输出格式不一致所致,千辛万苦在某一小角落发现这个方法,一针见血, ...

  5. python基础day3

    一.文件管理 文件管理是很多应用程序的基本功能和重要组成部分.Python可以使文件管理极其简单,特别是和其它语言相对比. 1.    读操作 1.1r模式 1.1.1读取其他路径下文件 首先在D盘创 ...

  6. .Karma+Jasmine+karma-coverage

    单元测试(模块测试)是开发者编写的一小段代码,用于检验被测代码的一个很小的.很明确的功能是否正确.通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为. Karma是一个基于N ...

  7. 基于TensorFlow的手写中文识别(版本一)

    具体效果实现: 第一次由于设备问题所以只训练了是一些个简单的字: 第二选了23个字训练了3000在字迹清晰下能够识别: 类似于默,鼠,鼓,这类文字也能识别,由于训练数据的问题,在测试的时候应尽量写在正 ...

  8. 3dmax 3dmax计算机要求 3dmax下载

    渲染首先是要X64兼容台式电脑,笔记本不行,笔记本就是学生拿来玩还行,渲染大图笔记本真的是发热. 配置一般的电脑和笔记本千万不要尝试安装3dmax2019了,很卡的,3dmax2019只有64位,没有 ...

  9. BZOJ.5397.circular(随机化 贪心)

    BZOJ 感觉自己完全没做过环上选线段的问题(除了一个2-SAT),所以来具体写一写qwq. 基本完全抄自remoon的题解qwq... (下标从\(0\sim m-1\)) 拆环为链,对于原线段\( ...

  10. DWM1000 帧过滤代码实现

    帧过滤功能可以在同一个环境内组建多个网络而不干扰(非频段不同),可以通过PANID(网络ID)区分不同网络,不同网络中的模块无法直接通信, 再之,利用短地址,网络中可以同时有多个模块发送信息,而接收端 ...