下面咱們來看一個思惟導圖,這樣就能夠更快了解所須要的功能:javascript
上一節咱們利用了一個簡單的代碼例子,完成了與微信公衆號的對話(給公衆號發一條信息,並獲得回覆)html
這一節將講解公衆號如何設置,雖然公衆號管理只是一張表,可是設計起來仍是有一些技巧java
1.一個企業可能底下有多個業務公衆號在同一個系統中須要處理(用戶發起的請求,是對應咱們哪一個公衆號)sql
2.多個公衆號下,後臺如何取得操做比較方便(設置當前公衆號爲默認操做號)數據庫
3.能夠手動刷新Access_Token,由於咱們隨時要保持Access_Token可用,這是調用微信接口的主要令牌(咱們後面將講解定時更新,而非手動)api
1.表設計緩存
2.設置爲默認公衆號服務器
3.生成指定格式的URL資源服務器微信
4.更新Access_Token app
表的設計沒有太多的成分,咱們根據公衆號的信息,本身創建對應的字段,下面是我已經已創建好的數據表
CREATE TABLE [dbo].[WC_OfficalAccounts]( [Id] [varchar](50) NOT NULL, --主鍵 [OfficalId] [varchar](200) NULL, --公衆號的惟一ID [OfficalName] [varchar](200) NOT NULL, --公衆號名稱 [OfficalCode] [varchar](200) NOT NULL, --公衆號賬號 [OfficalPhoto] [varchar](1000) NULL, --頭像 [OfficalKey] [varchar](500) NULL, --EncodingAESKey [ApiUrl] [varchar](1000) NULL, --咱們的資源服務器 [Token] [varchar](200) NULL, --Token [AppId] [varchar](200) NULL, --AppId [AppSecret] [varchar](200) NULL, --Appsecret [AccessToken] [varchar](200) NULL, --訪問Token [Remark] [varchar](2000) NULL, --說明 [Enable] [bit] NOT NULL, --是否啓用 [IsDefault] [bit] NOT NULL, --是否爲當前默認操做號 [Category] [int] NOT NULL, --類別(媒體號,企業號,我的號,開發測試號) [CreateTime] [datetime] NOT NULL, --建立時間 [CreateBy] [varchar](50) NOT NULL, --建立人 [ModifyTime] [datetime] NOT NULL, --修改時間 [ModifyBy] [varchar](50) NULL, --修改人 CONSTRAINT [PK_WC_OfficalAcconts] PRIMARY KEY CLUSTERED ( [Id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]
每一個字段我都有註釋,可是可能還不夠明白,咱們來看網站對應的數據,這樣更加清楚一點
這5個字段是必要的,其餘其實都是非必要的。我寫了那麼多,只是讓後臺管理可以得知當前操做號的信息狀況,因此到時建立的時候,這個是對應的填寫字段
操做EF的增刪該查,我在這裏就不作代碼演示了,已經在前面的相同功能演示過不少遍(下載尾部代碼或者本身動手作起來)
咱們來看主要的功能代碼。
這裏我以字段IsDefault來標識,哪一個公衆號爲系統當前的操做公衆號
第1、設置全部公衆號的IsDefault爲Flase
第2、設置指定ID的公衆號的IsDefault爲True
update [dbo].[WC_OfficalAccounts] set IsDefault=0
update [dbo].[WC_OfficalAccounts] set IsDefault=1 where id='XXXXXXX'
public bool SetDefault(string id) { //更新全部爲不默認0 ExecuteSqlCommand(@"update [dbo].[WC_OfficalAccounts] set IsDefault=0"); //設置當前爲默認1 return ExecuteSqlCommand(@"update [dbo].[WC_OfficalAccounts] set IsDefault=1 where id='"+id+"'")>0; }
咱們以前系統並未出現,這種直接執行Sql語句的,因此我寫了ExecuteSqlCommand這個方法(還有其餘幾個,可能之後會用到),擴展在倉儲中BaseRepository
/// <summary> /// 執行一條SQL語句 /// </summary> /// <param name="sql"></param> /// <returns></returns> public int ExecuteSqlCommand(string sql) { return Context.Database.ExecuteSqlCommand(sql); } /// <summary> /// 異步執行一條SQL語句 /// </summary> /// <param name="sql"></param> /// <returns></returns> public Task<int> ExecuteSqlCommandAsync(string sql) { return Context.Database.ExecuteSqlCommandAsync(sql); } public DbRawSqlQuery<T> SqlQuery(string sql) { return db.Database.SqlQuery<T>(sql); } /// <summary> /// 查詢一條語句返回結果集 /// </summary> /// <param name="sql"></param> /// <returns></returns> public DbRawSqlQuery<T> SqlQuery(string sql,params object[] paras) { return db.Database.SqlQuery<T>(sql,paras); }
這裏確定有人會問,你那個可能會被注入,沒錯,實際應該用參數的方式,可是我在過濾器處理了SQL的注入。
那麼在過濾器若是處理注入?這是擴展出另外一個問題了,若是感興趣,展開下面代碼(莫非是對傳入的參數進行格式處理)
var actionParameters = filterContext.ActionDescriptor.GetParameters(); foreach (var p in actionParameters) { if (p.ParameterType == typeof(string)) { if (filterContext.ActionParameters[p.ParameterName] != null) { filterContext.ActionParameters[p.ParameterName] = ResultHelper.Formatstr(filterContext.ActionParameters[p.ParameterName].ToString()); } } }
public static string Formatstr(string html) { System.Text.RegularExpressions.Regex regex1 = new System.Text.RegularExpressions.Regex(@"<script[\s\S]+</script *>", System.Text.RegularExpressions.RegexOptions.IgnoreCase); System.Text.RegularExpressions.Regex regex2 = new System.Text.RegularExpressions.Regex(@" href *= *[\s\S]*script *:", System.Text.RegularExpressions.RegexOptions.IgnoreCase); System.Text.RegularExpressions.Regex regex3 = new System.Text.RegularExpressions.Regex(@" on[\s\S]*=", System.Text.RegularExpressions.RegexOptions.IgnoreCase); System.Text.RegularExpressions.Regex regex4 = new System.Text.RegularExpressions.Regex(@"<iframe[\s\S]+</iframe *>", System.Text.RegularExpressions.RegexOptions.IgnoreCase); System.Text.RegularExpressions.Regex regex5 = new System.Text.RegularExpressions.Regex(@"<frameset[\s\S]+</frameset *>", System.Text.RegularExpressions.RegexOptions.IgnoreCase); System.Text.RegularExpressions.Regex regex10 = new System.Text.RegularExpressions.Regex(@"select", System.Text.RegularExpressions.RegexOptions.IgnoreCase); System.Text.RegularExpressions.Regex regex11 = new System.Text.RegularExpressions.Regex(@"update", System.Text.RegularExpressions.RegexOptions.IgnoreCase); System.Text.RegularExpressions.Regex regex12 = new System.Text.RegularExpressions.Regex(@"delete", System.Text.RegularExpressions.RegexOptions.IgnoreCase); html = regex1.Replace(html, ""); //過濾<script></script>標記 html = regex2.Replace(html, ""); //過濾href=javascript: (<A>) 屬性 html = regex3.Replace(html, " _disibledevent="); //過濾其它控件的on...事件 html = regex4.Replace(html, ""); //過濾iframe html = regex10.Replace(html, "s_elect"); html = regex11.Replace(html, "u_pudate"); html = regex12.Replace(html, "d_elete"); html = html.Replace("'", "’"); html = html.Replace(" ", " "); return html; }
業務層
public WC_OfficalAccountsModel GetCurrentAccount() { WC_OfficalAccounts entity = m_Rep.GetCurrentAccount(); if (entity == null) { return new WC_OfficalAccountsModel(); } WC_OfficalAccountsModel model = new WC_OfficalAccountsModel(); model.Id = entity.Id; model.OfficalName = entity.OfficalName; model.OfficalCode = entity.OfficalCode; model.OfficalPhoto = entity.OfficalPhoto; model.ApiUrl = entity.ApiUrl; model.Token = entity.Token; model.AppId = entity.AppId; model.AppSecret = entity.AppSecret; model.AccessToken = entity.AccessToken; model.Remark = entity.Remark; model.Enable = entity.Enable; model.IsDefault = entity.IsDefault; model.Category = entity.Category; model.CreateTime = entity.CreateTime; model.CreateBy = entity.CreateBy; model.ModifyTime = entity.ModifyTime; model.ModifyBy = entity.ModifyBy; return model; }
數據訪問層
public WC_OfficalAccounts GetCurrentAccount() { return Context.WC_OfficalAccounts.Where(p=>p.IsDefault).FirstOrDefault(); }
其餘代碼都由生成器生成,沒有爭議,也很簡單
上一節咱們用的是一個地址
http://ymnets.imwork.net/WC/WcChat
此次咱們這個地址要稍微改變一下,讓系統知道請求者發送的請求是來自哪一個公衆號:
http://ymnets.imwork.net/WC/WcChat?Id=XXXXXXXX (XXXXXXX是咱們系統本身定義的GUID)
這樣請求的時候就知道是請求哪一個ID
因此咱們的Post方法必須加上判斷代碼,並利用這個Id去獲取當前公衆號的信息
[HttpPost] [ActionName("Index")] public Task<ActionResult> Post(PostModel postModel) { return Task.Factory.StartNew<ActionResult>(() => { WC_OfficalAccountsModel model = account_BLL.GetCurrentAccount(); //沒有參數 if (string.IsNullOrEmpty(Request["id"])) { return new WeixinResult("非法路徑請求!"); } if (!CheckSignature.Check(postModel.Signature, postModel.Timestamp, postModel.Nonce, model.Token)) { return new WeixinResult("參數錯誤!"); } postModel.Token = Token; postModel.EncodingAESKey = EncodingAESKey; //根據本身後臺的設置保持一致 postModel.AppId = model.AppId; //根據本身後臺的設置保持一致 var messageHandler = new CustomMessageHandler(Request.InputStream, postModel, Request["id"], 10); messageHandler.Execute(); //執行微信處理過程 return new FixWeixinBugWeixinResult(messageHandler); }).ContinueWith<ActionResult>(task => task.Result); }
1.判斷URL是不是正確的
2.根據得到ID到數據庫或者(Redis,緩存)等得到公衆號信息
因爲咱們的訪問Token默認是2個小時過期,並且咱們不能時刻去微信服務器獲取
1.獲取的次數是有限制。因此咱們須要保存這個Token地址,在下次過時以前更新來永遠保持Access_Token的有效
2.這裏我保存在表裏面,在獲取當前操做號的時候順即可以得到這個Access_Token
他的樣子大約是這樣的:tZH82Jd6JQL6dPEU1hwhM9_jl0SrLRZV3j6-DV-EOQFUl3r37OXQNq-rpmjXEV2sVj1EQdxLotLCDMz_DdVAhZHQG6vgckOW8k90_9i24jP7giAOJM669zbiqc3HYW6wQDGeAJAYLO
如何得到Token:這裏咱們使用Senparc.WeiXin SDK的方法,
Senparc.Weixin.MP.CommonAPIs.CommonApi.GetToken(model.AppId, model.AppSecret).access_token;
一句話,得到當前操做號以後,利用AppId和Appsecret,微信服務器將給你一個Token。因此這個兩個東西是要打碼的,要不很麻煩。
雖然他幫咱們封裝了,可是不用他的方法,咱們本身也能夠直接調用微信的接口方法
var url = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type={0}&appid={1}&secret={2}", grant_type.AsUrlData(), appid.AsUrlData(), secret.AsUrlData()); AccessTokenResult result = Get.GetJson<AccessTokenResult>(url); return result;
這個接口,只有幾個參數,具體參數能夠查看幫助文檔 傳送門 成功返回:{"access_token":"ACCESS_TOKEN","expires_in":7200}
因此我這裏是一個更新當前全部公衆號的過程
public JsonResult GetToken() { List<WC_OfficalAccountsModel> list = m_BLL.GetList(ref setNoPagerAscById, ""); foreach (var model in list) { if (!string.IsNullOrEmpty(model.AppId) && !string.IsNullOrEmpty(model.AppSecret)) { model.AccessToken = Senparc.Weixin.MP.CommonAPIs.CommonApi.GetToken(model.AppId, model.AppSecret).access_token; model.ModifyTime = ResultHelper.NowTime; m_BLL.Edit(ref errors, model); } } return Json(JsonHandler.CreateMessage(1, "成批更新成功")); }
謝謝你們