
一、 基础配置
Web.config
<appSettings>
<!--微信公众号 输入公众号的值 -->
<add key="WeChat_Public_Token" value="Token" />
<add key="WeChat_Public_AppId" value="AppId" />
<add key="WeChat_Public_AppSecret" value="AppSecret" />
<add key="WeChat_Public_EncodingAESKey" value="EncodingAESKey" />
</appSettings>
自定义工具类
public class GetAppSettingHelper
{
public static string Token => ConfigurationManager.AppSettings["WeChat_Public_Token"];
public static string AppId => ConfigurationManager.AppSettings["WeChat_Public_AppId"];
public static string AppSecret =>ConfigurationManager.AppSettings["WeChat_Public_AppSecret"];
public static string EncodingAESKey => ConfigurationManager.AppSettings["WeChat_Public_EncodingAESKey"];
}
Global.asax
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
//全局注册Senparc.Weixin
var isGLobalDebug = true;
var senparcSetting = SenparcSetting.BuildFromWebConfig(isGLobalDebug);
IRegisterService register = RegisterService.Start(senparcSetting).UseSenparcGlobal();
var isWeixinDebug = true;
var senparcWeixinSetting = SenparcWeixinSetting.BuildFromWebConfig(isWeixinDebug);
register.UseSenparcWeixin(senparcWeixinSetting, senparcSetting);
//引入log4net日志处理配置文件
log4net.Config.XmlConfigurator.Configure(new System.IO.FileInfo(Server.MapPath("~/bin/Log4Net.config")));
}
}二、服务器配置
HomeController
public partial class HomeController : Controller
{
public static readonly string Token = GetAppSettingHelper.Token;
public static readonly string EncodingAESKey = GetAppSettingHelper.EncodingAESKey;
public static readonly string AppId = GetAppSettingHelper.AppId;
readonly Func<string> _getRandomFileName = () => SystemTime.Now.ToString("yyyyMMdd-HHmmss") + Guid.NewGuid().ToString("n").Substring(0, 6);
/// <summary>
/// 微信后台验证地址(使用Get)
/// </summary>
[HttpGet]
[ActionName("Index")]
public ActionResult Get(PostModel postModel, string echostr)
{
if (CheckSignature.Check(postModel.Signature, postModel.Timestamp, postModel.Nonce, Token))
{
return Content(echostr);
}
else
{
return Content("failed:" + postModel.Signature + "," + .CheckSignature.GetSignature(postModel.Timestamp, postModel.Nonce, Token) + "。" +
"如果你在浏览器中看到这句话,说明此地址可以被作为微信公众账号后台的Url,请注意保持Token一致。");
}
}
/// <summary>
/// 用户发送消息后,微信平台自动Post一个请求到这里,并等待响应XML。
/// PS:此方法为简化方法,效果与OldPost一致。
/// v0.8之后的版本可以结合.MvcExtension扩展包,使用WeixinResult,见MiniPost方法。
/// </summary>
[HttpPost]
[ActionName("Index")]
[Obsolete]
public ActionResult Post(PostModel postModel)
{
if (!CheckSignature.Check(postModel.Signature, postModel.Timestamp, postModel.Nonce, Token))
{
return Content("参数错误!");
}
#region 打包 PostModel 信息
postModel.Token = Token;//根据自己后台的设置保持一致
postModel.EncodingAESKey = EncodingAESKey;//根据自己后台的设置保持一致
postModel.AppId = AppId;//根据自己后台的设置保持一致
#endregion
//v4.2.2之后的版本,可以设置每个人上下文消息储存的最大数量,防止内存占用过多,如果该参数小于等于0,则不限制
var maxRecordCount = 10;
//自定义MessageHandler,对微信请求的详细判断操作都在这里面。
var messageHandler = new CustomMessageHandler(Request.InputStream, postModel, maxRecordCount);
#region 设置消息去重
/* 如果需要添加消息去重功能,只需打开OmitRepeatedMessage功能,SDK会自动处理。
* 收到重复消息通常是因为微信服务器没有及时收到响应,会持续发送2-5条不等的相同内容的RequestMessage*/
messageHandler.OmitRepeatedMessage = true;//默认已经开启,此处仅作为演示,也可以设置为false在本次请求中停用此功能
#endregion
try
{
messageHandler.SaveRequestMessageLog();//记录 Request 日志(可选)
messageHandler.Execute();//执行微信处理过程(关键)
messageHandler.SaveResponseMessageLog();//记录 Response 日志(可选)
return new FixWeixinBugWeixinResult(messageHandler);
}
catch (Exception ex)
{
#region 异常处理
WeixinTrace.Log("MessageHandler错误:{0}", ex.Message);
using (TextWriter tw = new StreamWriter(ServerUtility.ContentRootMapPath("~/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
}
}
}CustomMessageHandler.cs
public class CustomMessageHandler : MessageHandler<CustomMessageContext>
{
public CustomMessageHandler(Stream inputStream, PostModel postModel, int maxRecordCount = 0, bool onlyAllowEncryptMessage = false)
: base(inputStream, postModel, maxRecordCount, onlyAllowEncryptMessage)
{
//这里设置仅用于测试,实际开发可以在外部更全局的地方设置,
//比如MessageHandler<MessageContext>.GlobalGlobalMessageContext.ExpireMinutes = 3。
GlobalMessageContext.ExpireMinutes = 3;
OnlyAllowEncryptMessage = true;//是否只允许接收加密消息,默认为 false
if (!string.IsNullOrEmpty(postModel.AppId))
{
appId = postModel.AppId;//通过第三方开放平台发送过来的请求
}
//在指定条件下,不使用消息去重
base.OmitRepeatedMessageFunc = requestMessage =>
{
var textRequestMessage = requestMessage as RequestMessageText;
if (textRequestMessage != null && textRequestMessage.Content == "容错")
{
return false;
}
return true;
};
}
/// <summary>
/// 默认消息
/// </summary>
/// <param name="requestMessage"></param>
/// <returns></returns>
public override IResponseMessageBase DefaultResponseMessage(IRequestMessageBase requestMessage)
{
var responseMessage = this.CreateResponseMessage<ResponseMessageText>();
responseMessage.Content = "这条消息来自DefaultResponseMessage。";
return responseMessage;
}
//关注公众号
public override IResponseMessageBase OnEvent_SubscribeRequest(RequestMessageEvent_Subscribe requestMessage)
{
.....
}
}CustomMessageContext.cs
public class CustomMessageContext : DefaultMpMessageContext
{
public CustomMessageContext()
{
base.MessageContextRemoved += CustomMessageContext_MessageContextRemoved;
}
/// <summary>
/// 当上下文过期,被移除时触发的时间
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void CustomMessageContext_MessageContextRemoved(object sender, Senparc.NeuChar.Context.WeixinContextRemovedEventArgs<IRequestMessageBase, IResponseMessageBase> e)
{
/* 注意,这个事件不是实时触发的(当然你也可以专门写一个线程监控)
* 为了提高效率,根据WeixinContext中的算法,这里的过期消息会在过期后下一条请求执行之前被清除
*/
var messageContext = e.MessageContext as CustomMessageContext;
if (messageContext == null)
{
return;//如果是正常的调用,messageContext不会为null
}
//TODO:这里根据需要执行消息过期时候的逻辑,下面的代码仅供参考
//Log.InfoFormat("{0}的消息上下文已过期",e.OpenId);
//api.SendMessage(e.OpenId, "由于长时间未搭理客服,您的客服状态已退出!");
}
}
















