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
{
/// <summary>
/// 网络工具类。
/// </summary>
public sealed class WebUtils
{
private int _timeout = 100000;
/// <summary>
/// 请求与响应的超时时间
/// </summary>
public int Timeout
{
get { return this._timeout; }
set { this._timeout = value; }
}
/// <summary>
/// 执行HTTP POST请求。
/// </summary>
/// <param name="url">请求地址</param>
/// <param name="parameters">请求参数</param>
/// <returns>HTTP响应</returns>
public string DoPost(string url, IDictionary<string, string> parameters)
{
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);
}
/// <summary>
/// 执行HTTP GET请求。
/// </summary>
/// <param name="url">请求地址</param>
/// <param name="parameters">请求参数</param>
/// <returns>HTTP响应</returns>
public string DoGet(string url, IDictionary<string, string> parameters)
{
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;
}
/// <summary>
/// 把响应流转换为文本。
/// </summary>
/// <param name="rsp">响应流对象</param>
/// <param name="encoding">编码方式</param>
/// <returns>响应文本</returns>
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();
}
}
/// <summary>
/// 组装GET请求URL。
/// </summary>
/// <param name="url">请求地址</param>
/// <param name="parameters">请求参数</param>
/// <returns>带参数的GET请求URL</returns>
public string BuildGetUrl(string url, IDictionary<string, string> parameters)
{
if (parameters != null && parameters.Count > 0)
{
if (url.Contains("?"))
{
url = url + "&" + BuildQuery(parameters);
}
else
{
url = url + "?" + BuildQuery(parameters);
}
}
return url;
}
/// <summary>
/// 组装普通文本请求参数。
/// </summary>
/// <param name="parameters">Key-Value形式请求参数字典</param>
/// <returns>URL编码后的请求数据</returns>
public static string BuildQuery(IDictionary<string, string> parameters)
{
StringBuilder postData = new StringBuilder();
bool hasParam = false;
IEnumerator<KeyValuePair<string, string>> 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
{
/// <summary>
/// 错误码 {"errcode":40013,"errmsg":"invalid appid"}
/// </summary>
[XmlElement("errcode")]
public string ErrCode { get; set; }
/// <summary>
/// 错误信息
/// </summary>
[XmlElement("errmsg")]
public string ErrMsg { get; set; }
/// <summary>
/// 响应原始内容
/// </summary>
public string Body { get; set; }
/// <summary>
/// HTTP GET请求的URL
/// </summary>
public string ReqUrl { get; set; }
/// <summary>
/// 响应结果是否错误
/// </summary>
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
{
/// <summary>
/// UserBuyerGetResponse.
/// </summary>
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
{
/// <summary>
/// WXOP请求接口。
/// </summary>
public interface IWxopRequest<T> where T : WxopResponse
{
/// <summary>
/// 获取所有的Key-Value形式的文本请求参数字典。其中:
/// Key: 请求参数名
/// Value: 请求参数文本值
/// </summary>
/// <returns>文本请求参数字典</returns>
IDictionary<string, string> GetParameters();
}
}
接下来,建立一个请求客户端的接口IWxopClient.css,一个默认客户端 DefaultWxopClient.cs
using System;
using Wxop.Api.Request;
namespace Wxop.Api
{
/// <summary>
/// WXOP客户端。
/// </summary>
public interface IWxopClient
{
/// <summary>
/// 执行WXOP公开API请求。
/// </summary>
/// <typeparam name="T">领域对象</typeparam>
/// <param name="request">具体的WXOP API请求</param>
/// <returns>领域对象</returns>
T Execute<T>(IWxopRequest<T> 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
{
/// <summary>
/// 基于REST的WXOP客户端。
/// </summary>
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<T>(IWxopRequest<T> request) where T : WxopResponse
{
try
{
return DoExecute<T>(request);
}
catch (Exception e)
{
throw e;
}
}
#endregion
private T DoExecute<T>(IWxopRequest<T> 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<T>();
rsp.Body = body;
}
else
{
if (FORMAT_XML.Equals(format))
{
IWxopParser tp = new WxopXmlParser();
rsp = tp.Parse<T>(body);
}
else
{
IWxopParser tp = new WxopJsonParser();
rsp = tp.Parse<T>(body);
}
}
return rsp;
}
}
}
这里边用到了Json解析,直接使用的Jayrock.Json组件。建立了一个JSON通用解释器。IWxopParser.cs IWxopReader.cs WxopJsonParser.cs
using System;
namespace Wxop.Api.Parser
{
/// <summary>
/// WXOP API响应解释器接口。响应格式可以是XML, JSON等等。
/// </summary>
public interface IWxopParser
{
/// <summary>
/// 把响应字符串解释成相应的领域对象。
/// </summary>
/// <typeparam name="T">领域对象</typeparam>
/// <param name="body">响应字符串</param>
/// <returns>领域对象</returns>
T Parse<T>(string body) where T : WxopResponse;
}
}
using System;
using System.Collections;
namespace Wxop.Api.Parser
{
public delegate object DWxopConvert(IWxopReader reader, Type type);
/// <summary>
/// WXOP API响应读取器接口。响应格式可以是XML, JSON等等。
/// </summary>
public interface IWxopReader
{
/// <summary>
/// 判断响应中是否包含指定的属性。
/// </summary>
/// <param name="name">属性名称</param>
/// <returns>true/false</returns>
bool HasReturnField(object name);
/// <summary>
/// 获取值类型属性的值。
/// </summary>
/// <param name="name">属性名称</param>
/// <returns>值对象</returns>
object GetPrimitiveObject(object name);
/// <summary>
/// 获取引用类型的值。
/// </summary>
/// <param name="name">属性名称</param>
/// <param name="type">引用类型</param>
/// <param name="convert">转换器</param>
/// <returns>引用对象</returns>
object GetReferenceObject(object name, Type type, DWxopConvert convert);
/// <summary>
/// 获取列表类型的值。
/// </summary>
/// <param name="listName">列表属性名称</param>
/// <param name="itemName">列表项名称</param>
/// <param name="type">引用类型</param>
/// <param name="convert">转换器</param>
/// <returns>列表对象</returns>
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
{
/// <summary>
/// WXOP JSON响应通用解释器。
/// </summary>
public class WxopJsonParser : IWxopParser
{
private static readonly Dictionary<string, Dictionary<string, WxopAttribute>> attrs = new Dictionary<string, Dictionary<string, WxopAttribute>>();
#region IWxopParser Members
public T Parse<T>(string body) where T : WxopResponse
{
return Parse<T>(body, false);
}
/// <summary>
/// JSON解释器,将JSON结果转为相关类 本方法为添加重载方法 Lasko 2013.11.22
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="body"></param>
/// <param name="removeRoot"></param>
/// <returns></returns>
public T Parse<T>(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<T>();
}
if (rsp != null)
{
rsp.Body = body;
}
return rsp;
}
#endregion
private Dictionary<string, WxopAttribute> GetWxopAttributes(Type type)
{
Dictionary<string, WxopAttribute> tas = null;
bool inc = attrs.TryGetValue(type.FullName, out tas);
if (inc && tas != null) // 从缓存中获取类属性元数据
{
return tas;
}
else // 创建新的类属性元数据缓存
{
tas = new Dictionary<string, WxopAttribute>();
}
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<string, WxopAttribute> pas = GetWxopAttributes(type);
Dictionary<string, WxopAttribute>.Enumerator em = pas.GetEnumerator();
while (em.MoveNext())
{
KeyValuePair<string, WxopAttribute> 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<string, WxopAttribute> pas = GetWxopAttributes(type);
Dictionary<string, WxopAttribute>.Enumerator em = pas.GetEnumerator();
while (em.MoveNext())
{
KeyValuePair<string, WxopAttribute> 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
{
/// <summary>
/// WXOP API: Get Access Token
/// </summary>
public class AccessTokenGetRequest : IWxopRequest<AccessTokenGetResponse>
{
private IDictionary<string, string> otherParameters;
#region IWxopRequest Members
public IDictionary<string, string> 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的储存了。我先想想解决方案。
浙公网安备 33010602011771号