access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token。正常情况下access_token有效期为7200秒,重复获取将导致上次获取的access_token失效。由于获取access_token的api调用次数非常有限,建议开发者全局存储与更新access_token,频繁刷新access_token会导致api调用受限,影响自身业务。
http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96access_token
按照官方给我们的说法,所有后续的操作,都要用到Access_Token,于是决定先解决这个问题。官方给的API如下:
这个过程,只要一个返回值,所以,代码也先简单写一下。只为Get到代码嘛。
继续在WxController中新建一个GetToken,返回值依然用String,便于测试。
private bool CheckHTTPS(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) { return true; } public string GetToken() { string strResult = ""; string strAppId = "wxfb90abc********"; string strAppsecret = "51b12f574e2c918571***********"; string url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"; url+= "&appid="+strAppId; url += "&secret=" + strAppsecret; ServicePointManager.ServerCertificateValidatiOnCallback= new RemoteCertificateValidationCallback(CheckHTTPS); HttpWebRequest req = (HttpWebRequest)WebRequest.CreateDefault(new Uri(url)); req.ServicePoint.Expect100COntinue= false; req.Method = "GET"; req.KeepAlive = true; req.UserAgent = "Wxop4Net"; req.Timeout = 100000; req.COntentType= "application/x-www-form-urlencoded;charset=utf-8"; HttpWebResponse rsp = (HttpWebResponse)req.GetResponse(); Encoding encoding = Encoding.UTF8; System.IO.Stream stream = null; StreamReader reader = null; try { // 以字符流的方式读取HTTP响应 stream = rsp.GetResponseStream(); reader = new StreamReader(stream, encoding); strResult = reader.ReadToEnd(); } finally { // 释放资源 if (reader != null) reader.Close(); if (stream != null) stream.Close(); if (rsp != null) rsp.Close(); } return strResult; }
这里多了一个方法,因为鹅厂用的是HTTPS的协议,为了减少验证的麻烦,回调方法直接返回ture,方便使用。此时,发布一下代码,上传服务器,访问一下。
结果返回了一个Token,这样,验证的基础方法就获得了,但此时要考虑的问题好像该多了一些。
第一,获取Token需要保存数据,进行网络存储,以备调用接口时使用。
第二,请求过程中,请求代码过于复杂,没有结构,该梳理一下。
基于以上,先解决第二个,需要把请求代码,和数据格式进得重新整理,于是决定将微信请求的API方法提取出来,单独一个类库(注:网上有很多现成的类库,但个人喜欢能看到内部,所以自己尝试着写一写)。
项目由原有的一个MVC项目,增加一个类库项目,定名WxpSdk。
第一步,建立网络工具类,WebUtils.cs,这个,各种项目经常用到,直接分离出一个就可以。
using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Net; using System.Text; using System.Web; using System.Net.Security; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; namespace Wxop.Api.Util { ////// 网络工具类。 /// public sealed class WebUtils { private int _timeout = 100000; ////// 请求与响应的超时时间 /// public int Timeout { get { return this._timeout; } set { this._timeout = value; } } ////// 执行HTTP POST请求。 /// /// 请求地址 /// 请求参数 ///HTTP响应 public string DoPost(string url, IDictionaryparameters) { HttpWebRequest req = GetWebRequest(url, "POST"); req.COntentType= "application/x-www-form-urlencoded;charset=utf-8"; byte[] postData = Encoding.UTF8.GetBytes(BuildQuery(parameters)); System.IO.Stream reqStream = req.GetRequestStream(); reqStream.Write(postData, 0, postData.Length); reqStream.Close(); HttpWebResponse rsp = (HttpWebResponse)req.GetResponse(); Encoding encoding = Encoding.GetEncoding(rsp.CharacterSet); return GetResponseAsString(rsp, encoding); } /// /// 执行HTTP GET请求。 /// /// 请求地址 /// 请求参数 ///HTTP响应 public string DoGet(string url, IDictionaryparameters) { if (parameters != null && parameters.Count > 0) { if (url.Contains("?")) { url = url + "&" + BuildQuery(parameters); } else { url = url + "?" + BuildQuery(parameters); } } HttpWebRequest req = GetWebRequest(url, "GET"); req.COntentType= "application/x-www-form-urlencoded;charset=utf-8"; HttpWebResponse rsp = (HttpWebResponse)req.GetResponse(); Encoding encoding = Encoding.UTF8; return GetResponseAsString(rsp, encoding); } public bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) { return true; } public HttpWebRequest GetWebRequest(string url, string method) { HttpWebRequest req = null; if (url.Contains("https")) { ServicePointManager.ServerCertificateValidatiOnCallback= new RemoteCertificateValidationCallback(CheckValidationResult); req = (HttpWebRequest)WebRequest.CreateDefault(new Uri(url)); } else { req = (HttpWebRequest)WebRequest.Create(url); } req.ServicePoint.Expect100COntinue= false; req.Method = method; req.KeepAlive = true; req.UserAgent = "Wxop4Net"; req.Timeout = this._timeout; return req; } /// /// 把响应流转换为文本。 /// /// 响应流对象 /// 编码方式 ///响应文本 public string GetResponseAsString(HttpWebResponse rsp, Encoding encoding) { System.IO.Stream stream = null; StreamReader reader = null; try { // 以字符流的方式读取HTTP响应 stream = rsp.GetResponseStream(); reader = new StreamReader(stream, encoding); return reader.ReadToEnd(); } finally { // 释放资源 if (reader != null) reader.Close(); if (stream != null) stream.Close(); if (rsp != null) rsp.Close(); } } ////// 组装GET请求URL。 /// /// 请求地址 /// 请求参数 ///带参数的GET请求URL public string BuildGetUrl(string url, IDictionaryparameters) { if (parameters != null && parameters.Count > 0) { if (url.Contains("?")) { url = url + "&" + BuildQuery(parameters); } else { url = url + "?" + BuildQuery(parameters); } } return url; } /// /// 组装普通文本请求参数。 /// /// Key-Value形式请求参数字典 ///URL编码后的请求数据 public static string BuildQuery(IDictionaryparameters) { StringBuilder postData = new StringBuilder(); bool hasParam = false; IEnumerator > dem = parameters.GetEnumerator(); while (dem.MoveNext()) { string name = dem.Current.Key; string value = dem.Current.Value; // 忽略参数名或参数值为空的参数 if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(value)) { if (hasParam) { postData.Append("&"); } postData.Append(name); postData.Append("="); postData.Append(HttpUtility.UrlEncode(value, Encoding.UTF8)); hasParam = true; } } return postData.ToString(); } } }
第二步,因为微信平台上,各种错误都会返回ErrCode,于是,先建立响应类的基类WxopResponse.cs
using System; using System.Xml.Serialization; namespace Wxop.Api { [Serializable] public abstract class WxopResponse { ////// 错误码 {"errcode":40013,"errmsg":"invalid appid"} /// [XmlElement("errcode")] public string ErrCode { get; set; } ////// 错误信息 /// [XmlElement("errmsg")] public string ErrMsg { get; set; } ////// 响应原始内容 /// public string Body { get; set; } ////// HTTP GET请求的URL /// public string ReqUrl { get; set; } ////// 响应结果是否错误 /// public bool IsError { get { return !string.IsNullOrEmpty(this.ErrCode); } } } }
建立AccessToken的响应类AccessTokenGetResponse.cs
using System; using System.Xml.Serialization; using Wxop.Api.Domain; namespace Wxop.Api.Response { ////// UserBuyerGetResponse. /// public class AccessTokenGetResponse : WxopResponse { [XmlElement("access_token")] public string AccessToken { get; set; } [XmlElement("expires_in")] public int ExpiresIn { get; set; } } }
接下来,建立一个请求类的接口 IWxopRequest.cs
using System.Collections.Generic; namespace Wxop.Api.Request { ////// WXOP请求接口。 /// public interface IWxopRequestwhere T : WxopResponse { /// /// 获取所有的Key-Value形式的文本请求参数字典。其中: /// Key: 请求参数名 /// Value: 请求参数文本值 /// ///文本请求参数字典 IDictionaryGetParameters(); } }
接下来,建立一个请求客户端的接口IWxopClient.css,一个默认客户端 DefaultWxopClient.cs
using System; using Wxop.Api.Request; namespace Wxop.Api { ////// WXOP客户端。 /// public interface IWxopClient { ////// 执行WXOP公开API请求。 /// ///领域对象 /// 具体的WXOP API请求 ///领域对象 T Execute(IWxopRequest request) where T : WxopResponse; } }
using System; using System.Collections; using System.Collections.Generic; using System.Xml; using Jayrock.Json.Conversion; using Wxop.Api.Parser; using Wxop.Api.Request; using Wxop.Api.Util; namespace Wxop.Api { ////// 基于REST的WXOP客户端。 /// public class DefaultWxopClient : IWxopClient { public const string FORMAT_XML = "xml"; public const string GRANT_TYPE="grant_type"; public const string APP_ID="appid"; public const string APP_SECRET="secret"; private string serverUrl; private string appKey; private string appSecret; private string format = FORMAT_XML; private WebUtils webUtils; private bool disableParser = false; // 禁用响应结果解释 #region DefaultWxopClient Constructors public DefaultWxopClient(string serverUrl, string appKey, string appSecret) { this.appKey = appKey; this.appSecret = appSecret; this.serverUrl = serverUrl; this.webUtils = new WebUtils(); } public DefaultWxopClient(string serverUrl, string appKey, string appSecret, string format) : this(serverUrl, appKey, appSecret) { this.format = format; } #endregion #region IWxopClient Members public T Execute(IWxopRequest request) where T : WxopResponse { try { return DoExecute (request); } catch (Exception e) { throw e; } } #endregion private T DoExecute (IWxopRequest request) where T : WxopResponse { // 添加协议级请求参数 WxopDictionary txtParams = new WxopDictionary(request.GetParameters()); txtParams.Add(GRANT_TYPE, "client_credential"); txtParams.Add(APP_ID, appKey); txtParams.Add(APP_SECRET, appSecret); string body = webUtils.DoGet(this.serverUrl, txtParams); // 解释响应结果 T rsp; if (disableParser) { rsp = Activator.CreateInstance (); rsp.Body = body; } else { if (FORMAT_XML.Equals(format)) { IWxopParser tp = new WxopXmlParser(); rsp = tp.Parse (body); } else { IWxopParser tp = new WxopJsonParser(); rsp = tp.Parse (body); } } return rsp; } } }
这里边用到了Json解析,直接使用的Jayrock.Json组件。建立了一个JSON通用解释器。IWxopParser.cs IWxopReader.cs WxopJsonParser.cs
using System; namespace Wxop.Api.Parser { ////// WXOP API响应解释器接口。响应格式可以是XML, JSON等等。 /// public interface IWxopParser { ////// 把响应字符串解释成相应的领域对象。 /// ///领域对象 /// 响应字符串 ///领域对象 T Parse(string body) where T : WxopResponse; } }
using System; using System.Collections; namespace Wxop.Api.Parser { public delegate object DWxopConvert(IWxopReader reader, Type type); ////// WXOP API响应读取器接口。响应格式可以是XML, JSON等等。 /// public interface IWxopReader { ////// 判断响应中是否包含指定的属性。 /// /// 属性名称 ///true/false bool HasReturnField(object name); ////// 获取值类型属性的值。 /// /// 属性名称 ///值对象 object GetPrimitiveObject(object name); ////// 获取引用类型的值。 /// /// 属性名称 /// 引用类型 /// 转换器 ///引用对象 object GetReferenceObject(object name, Type type, DWxopConvert convert); ////// 获取列表类型的值。 /// /// 列表属性名称 /// 列表项名称 /// 引用类型 /// 转换器 ///列表对象 IList GetListObjects(string listName, string itemName, Type type, DWxopConvert convert); } }
using System; using System.Collections; using System.Collections.Generic; using System.Reflection; using System.Xml.Serialization; using Jayrock.Json.Conversion; namespace Wxop.Api.Parser { ////// WXOP JSON响应通用解释器。 /// public class WxopJsonParser : IWxopParser { private static readonly Dictionary> attrs = new Dictionary >(); #region IWxopParser Members public T Parse (string body) where T : WxopResponse { return Parse (body, false); } /// /// JSON解释器,将JSON结果转为相关类 本方法为添加重载方法 Lasko 2013.11.22 /// ////// /// /// public T Parse (string body, bool removeRoot) where T : WxopResponse { T rsp = null; IDictionary json = JsonConvert.Import(body) as IDictionary; if (json != null) { IDictionary data = null; if (removeRoot) { //忽略根节点的名称 foreach (object key in json.Keys) { data = json[key] as IDictionary; break; } } else { data = json; } if (data != null) { IWxopReader reader = new WxopJsonReader(data); if (removeRoot){ rsp = (T)FromJson(reader, typeof(T)); } else { rsp = (T)FromJson(reader, typeof(T)); } } } if (rsp == null) { rsp = Activator.CreateInstance (); } if (rsp != null) { rsp.Body = body; } return rsp; } #endregion private Dictionary GetWxopAttributes(Type type) { Dictionary tas = null; bool inc = attrs.TryGetValue(type.FullName, out tas); if (inc && tas != null) // 从缓存中获取类属性元数据 { return tas; } else // 创建新的类属性元数据缓存 { tas = new Dictionary (); } PropertyInfo[] pis = type.GetProperties(); foreach (PropertyInfo pi in pis) { WxopAttribute ta = new WxopAttribute(); ta.Method = pi.GetSetMethod(); // 获取对象属性名称 XmlElementAttribute[] xeas = pi.GetCustomAttributes(typeof(XmlElementAttribute), true) as XmlElementAttribute[]; if (xeas != null && xeas.Length > 0) { ta.ItemName = xeas[0].ElementName; } // 获取列表属性名称 if (ta.ItemName == null) { XmlArrayItemAttribute[] xaias = pi.GetCustomAttributes(typeof(XmlArrayItemAttribute), true) as XmlArrayItemAttribute[]; if (xaias != null && xaias.Length > 0) { ta.ItemName = xaias[0].ElementName; } XmlArrayAttribute[] xaas = pi.GetCustomAttributes(typeof(XmlArrayAttribute), true) as XmlArrayAttribute[]; if (xaas != null && xaas.Length > 0) { ta.ListName = xaas[0].ElementName; } if (ta.ListName == null) { continue; } } // 获取属性类型 if (pi.PropertyType.IsGenericType) { Type[] types = pi.PropertyType.GetGenericArguments(); ta.ListType = types[0]; } else { ta.ItemType = pi.PropertyType; } tas.Add(pi.Name, ta); } attrs[type.FullName] = tas; return tas; } public object FromJson(IWxopReader reader, Type type) { object rsp = null; Dictionary pas = GetWxopAttributes(type); Dictionary .Enumerator em = pas.GetEnumerator(); while (em.MoveNext()) { KeyValuePair kvp = em.Current; WxopAttribute ta = kvp.Value; string itemName = ta.ItemName; string listName = ta.ListName; if (!reader.HasReturnField(itemName) && (string.IsNullOrEmpty(listName) || !reader.HasReturnField(listName))) { continue; } object value = null; if (ta.ListType != null) { value = reader.GetListObjects(listName, itemName, ta.ListType, FromJson); } else { if (typeof(string) == ta.ItemType) { object tmp = reader.GetPrimitiveObject(itemName); if (tmp != null) { value = tmp.ToString(); } } else if (typeof(long) == ta.ItemType) { object tmp = reader.GetPrimitiveObject(itemName); if (tmp != null) { value = ((IConvertible)tmp).ToInt64(null); } } else if (typeof(int) == ta.ItemType) { object tmp = reader.GetPrimitiveObject(itemName); if (tmp != null) { value = ((IConvertible)tmp).ToInt32(null); } } else if (typeof(bool) == ta.ItemType) { value = reader.GetPrimitiveObject(itemName); } else { value = reader.GetReferenceObject(itemName, ta.ItemType, FromJson); } } if (value != null) { if (rsp == null) { rsp = Activator.CreateInstance(type); } ta.Method.Invoke(rsp, new object[] { value }); } } return rsp; } public object FromJson(IWxopReader reader, Type type, bool haveChildNode) { object rsp = null; Dictionary pas = GetWxopAttributes(type); Dictionary .Enumerator em = pas.GetEnumerator(); while (em.MoveNext()) { KeyValuePair kvp = em.Current; WxopAttribute ta = kvp.Value; string itemName = ta.ItemName; string listName = ta.ListName; if (!reader.HasReturnField(itemName) && (string.IsNullOrEmpty(listName) || !reader.HasReturnField(listName))) { continue; } object value = null; if (ta.ListType != null) { value = reader.GetListObjects(listName, itemName, ta.ListType, FromJson); } else { if (typeof(string) == ta.ItemType) { object tmp = reader.GetPrimitiveObject(itemName); if (tmp != null) { value = tmp.ToString(); } } else if (typeof(long) == ta.ItemType) { object tmp = reader.GetPrimitiveObject(itemName); if (tmp != null) { value = ((IConvertible)tmp).ToInt64(null); } } else if (typeof(int) == ta.ItemType) { object tmp = reader.GetPrimitiveObject(itemName); if (tmp != null) { value = ((IConvertible)tmp).ToInt32(null); } } else if (typeof(bool) == ta.ItemType) { value = reader.GetPrimitiveObject(itemName); } else { value = reader.GetReferenceObject(itemName, ta.ItemType, FromJson); } } if (value != null) { if (rsp == null) { rsp = Activator.CreateInstance(type); } ta.Method.Invoke(rsp, new object[] { value }); } } return rsp; } } }
建立AccessToken 请求类 AccessTokenGetRequest.css
using System; using System.Collections.Generic; using Wxop.Api.Response; using Wxop.Api.Util; namespace Wxop.Api.Request { ////// WXOP API: Get Access Token /// public class AccessTokenGetRequest : IWxopRequest{ private IDictionary otherParameters; #region IWxopRequest Members public IDictionary GetParameters() { WxopDictionary parameters = new WxopDictionary(); //parameters.Add("fields", this.Fields); parameters.AddAll(this.otherParameters); return parameters; } #endregion public void AddOtherParameter(string key, string value) { if (this.otherParameters == null) { this.otherParameters = new WxopDictionary(); } this.otherParameters.Add(key, value); } } }
好了 ,准备工作都做好了,在原有的MVC项目里,引入刚整理好的类库。 GetToken的方法,得写为如下代码:
public string GetToken() { string strResult = String.Empty; string url = "https://api.weixin.qq.com/cgi-bin/token"; string appkey = "wxfb90abc********"; string appsecret = "51b12f574e2c9185********"; IWxopClient client = new DefaultWxopClient(url, appkey, appsecret, "json"); AccessTokenGetRequest req = new AccessTokenGetRequest(); AccessTokenGetResponse respOnse= client.Execute(req); if (!String.IsNullOrEmpty(response.AccessToken)) { strResult = response.AccessToken; } return strResult; }
再次执行,返回的就只有一个Token了。
至此,还需要解决的一个问题就是Token的储存了。我先想想解决方案。