微信最近比较热,但是目前公布的基于.net的微信SDK做的比较繁琐,本人打算实现一个轻量级,简单明了的SDK,真正的懒人思想,为懒人服务。

一、SDK要实现的目标

1.轻量级:这个概念比较热,相信大家都不想为了开发微信公众平台而去引用一大串的类,引用啥的,搞的项目异常臃肿。现在现有的SDK都犯了一个错误:就是把所有的数据交换的实体:包含微信服务器返回的xml数据,我们提交给微信服务器的json,xml数据都搞成强类型,这样做虽然开发者在写代码的时候能用.就可以访问到数据,但是这样做一个最大的弊端就是增大了SDK体量,不够简明,不够轻量级。而且维护起来很不不方便。对此:我提出了,使用.net的dynamic特性,就像你使用python,json一样,完全的不用任何强类型,SDK就会小很多,彻底实现轻量级这一目标。当然你会说,那这样的话,要手写,我要关心实体的property的拼写。所以说都各有利弊,主要看你自己觉得那种方式更适合你。

2.简单明了:SDK仅对官方API做适当包装,不过度包装,SDK布局和官方API文档保持一致,便于开发者查找。使开发者感觉在使用SDK时,可以方便快捷的找到。不至于向一些现有的SDK一样,让人摸不着他的设计思想。

微信公众平台开发者文档结构如下图:

其实,他主要是围绕用户、消息、公众号这三大块来的。

a.用户:用户管理和用户微信端公众号功能菜单展开,分别对应用户管理和自定义菜单。

b.消息:我们自己的服务器接受微信服务器post过来的消息(用户的消息是先提交到微信服务器,然后微信服务器在转发给我们的服务器),我们接收到消息,回复用户消息(还是一样,先提交到微信服务器,然后微信服务器在发送给用户),搞懂这一点很重要。我开发时,因为这个居然蠢到用session跟踪用户信息。

c.公众号:包括订阅号和服务号(现在又出企业号了),不同的号,调用API的权限不一样,这一点还需要开发者自己多多注意,也就是说,开发者虽然有了SDK,但不要完全不理会官方文档。自己帐号没有调用权限,非要说是SDK写的不好。

二、具体实现:

在博客园上发过一遍文章:

超级懒汉编写的基于.NET的微信SDK

  由于微信服务器Post给我们的是xml消息,返回的是json,而我们程序用的又是Dynamic,所以需要互转。这样就存在3种类型的format,这也是大量的框架定义实体类导致框架不够轻量级的的原因之所在。实现第一个目标,我主要用到了.net Framework4.0的Dynamic特性,和一个将xml字符串自动转换成Dynamic Object的DynamicXml.cs类,还有一个将json字符串自动转换成Dynamic Object的DynamicJson.cs类。苦苦寻觅,终于让我找到了我想要的。
1.以下是DynamicXml.cs类,文件头有原作者的版权信息。
</pre><p><pre name="code" class="csharp">/*--------------------------------------------------------------------------* https://www.captechconsulting.com/blog/kevin-hazzard/fluent-xml-parsing-using-cs-dynamic-type-part-1* 博客园网友 夜の魔王 友情借用此代码,用于微信开发。* http://www.weixinsdk.net/
*--------------------------------------------------------------------------*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Dynamic;
using System.Xml.Linq;
using System.Collections;public class DynamicXml : DynamicObject, IEnumerable
{private readonly List<XElement> _elements;public DynamicXml(string text){var doc = XDocument.Parse(text);_elements = new List<XElement> { doc.Root };}protected DynamicXml(XElement element){_elements = new List<XElement> { element };}protected DynamicXml(IEnumerable<XElement> elements){_elements = new List<XElement>(elements);}public override bool TryGetMember(GetMemberBinder binder, out object result){result = null;if (binder.Name == "Value")result = _elements[0].Value;else if (binder.Name == "Count")result = _elements.Count;else{var attr = _elements[0].Attribute(XName.Get(binder.Name));if (attr != null)result = attr;else{var items = _elements.Descendants(XName.Get(binder.Name));if (items == null || items.Count() == 0) return false;result = new DynamicXml(items);}}return true;}public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result){int ndx = (int)indexes[0];result = new DynamicXml(_elements[ndx]);return true;}public IEnumerator GetEnumerator(){foreach (var element in _elements)yield return new DynamicXml(element);}
}

2. 以下是DynamicJson.cs类,文件头有原作者的版权信息

/*--------------------------------------------------------------------------
* DynamicJson
* ver 1.2.0.0 (May. 21th, 2010)
*
* created and maintained by neuecc <ils@neue.cc>
* licensed under Microsoft Public License(Ms-PL)
* http://neue.cc/
* http://dynamicjson.codeplex.com/*更多: http://www.weixinsdk.net*QQ: 2586662969*Email: 2586662969@qq.com
*--------------------------------------------------------------------------*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Xml;
using System.Xml.Linq;namespace Codeplex.Data
{public class DynamicJson : DynamicObject{private enum JsonType{@string, number, boolean, @object, array, @null}// public static methods/// <summary>from JsonSring to DynamicJson</summary>public static dynamic Parse(string json){return Parse(json, Encoding.Unicode);}/// <summary>from JsonSring to DynamicJson</summary>public static dynamic Parse(string json, Encoding encoding){using (var reader = JsonReaderWriterFactory.CreateJsonReader(encoding.GetBytes(json), XmlDictionaryReaderQuotas.Max)){return ToValue(XElement.Load(reader));}}/// <summary>from JsonSringStream to DynamicJson</summary>public static dynamic Parse(Stream stream){using (var reader = JsonReaderWriterFactory.CreateJsonReader(stream, XmlDictionaryReaderQuotas.Max)){return ToValue(XElement.Load(reader));}}/// <summary>from JsonSringStream to DynamicJson</summary>public static dynamic Parse(Stream stream, Encoding encoding){using (var reader = JsonReaderWriterFactory.CreateJsonReader(stream, encoding, XmlDictionaryReaderQuotas.Max, _ => { })){return ToValue(XElement.Load(reader));}}/// <summary>create JsonSring from primitive or IEnumerable or Object({public property name:property value})</summary>public static string Serialize(object obj){return CreateJsonString(new XStreamingElement("root", CreateTypeAttr(GetJsonType(obj)), CreateJsonNode(obj)));}// private static methodsprivate static dynamic ToValue(XElement element){var type = (JsonType)Enum.Parse(typeof(JsonType), element.Attribute("type").Value);switch (type){case JsonType.boolean:return (bool)element;case JsonType.number:return (double)element;case JsonType.@string:return (string)element;case JsonType.@object:case JsonType.array:return new DynamicJson(element, type);case JsonType.@null:default:return null;}}private static JsonType GetJsonType(object obj){if (obj == null) return JsonType.@null;switch (Type.GetTypeCode(obj.GetType())){case TypeCode.Boolean:return JsonType.boolean;case TypeCode.String:case TypeCode.Char:case TypeCode.DateTime:return JsonType.@string;case TypeCode.Int16:case TypeCode.Int32:case TypeCode.Int64:case TypeCode.UInt16:case TypeCode.UInt32:case TypeCode.UInt64:case TypeCode.Single:case TypeCode.Double:case TypeCode.Decimal:case TypeCode.SByte:case TypeCode.Byte:return JsonType.number;case TypeCode.Object:return (obj is IEnumerable) ? JsonType.array : JsonType.@object;case TypeCode.DBNull:case TypeCode.Empty:default:return JsonType.@null;}}private static XAttribute CreateTypeAttr(JsonType type){return new XAttribute("type", type.ToString());}private static object CreateJsonNode(object obj){var type = GetJsonType(obj);switch (type){case JsonType.@string:case JsonType.number:return obj;case JsonType.boolean:return obj.ToString().ToLower();case JsonType.@object:return CreateXObject(obj);case JsonType.array:return CreateXArray(obj as IEnumerable);case JsonType.@null:default:return null;}}private static IEnumerable<XStreamingElement> CreateXArray<T>(T obj) where T : IEnumerable{return obj.Cast<object>().Select(o => new XStreamingElement("item", CreateTypeAttr(GetJsonType(o)), CreateJsonNode(o)));}private static IEnumerable<XStreamingElement> CreateXObject(object obj){return obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(pi => new { Name = pi.Name, Value = pi.GetValue(obj, null) }).Select(a => new XStreamingElement(a.Name, CreateTypeAttr(GetJsonType(a.Value)), CreateJsonNode(a.Value)));}private static string CreateJsonString(XStreamingElement element){using (var ms = new MemoryStream())using (var writer = JsonReaderWriterFactory.CreateJsonWriter(ms, Encoding.Unicode)){element.WriteTo(writer);writer.Flush();return Encoding.Unicode.GetString(ms.ToArray());}}// dynamic structure represents JavaScript Object/Arrayreadonly XElement xml;readonly JsonType jsonType;/// <summary>create blank JSObject</summary>public DynamicJson(){xml = new XElement("root", CreateTypeAttr(JsonType.@object));jsonType = JsonType.@object;}private DynamicJson(XElement element, JsonType type){Debug.Assert(type == JsonType.array || type == JsonType.@object);xml = element;jsonType = type;}public bool IsObject { get { return jsonType == JsonType.@object; } }public bool IsArray { get { return jsonType == JsonType.array; } }/// <summary>has property or not</summary>public bool IsDefined(string name){return IsObject && (xml.Element(name) != null);}/// <summary>has property or not</summary>public bool IsDefined(int index){return IsArray && (xml.Elements().ElementAtOrDefault(index) != null);}/// <summary>delete property</summary>public bool Delete(string name){var elem = xml.Element(name);if (elem != null){elem.Remove();return true;}else return false;}/// <summary>delete property</summary>public bool Delete(int index){var elem = xml.Elements().ElementAtOrDefault(index);if (elem != null){elem.Remove();return true;}else return false;}/// <summary>mapping to Array or Class by Public PropertyName</summary>public T Deserialize<T>(){return (T)Deserialize(typeof(T));}private object Deserialize(Type type){return (IsArray) ? DeserializeArray(type) : DeserializeObject(type);}private dynamic DeserializeValue(XElement element, Type elementType){var value = ToValue(element);if (value is DynamicJson){value = ((DynamicJson)value).Deserialize(elementType);}return Convert.ChangeType(value, elementType);}private object DeserializeObject(Type targetType){var result = Activator.CreateInstance(targetType);var dict = targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanWrite).ToDictionary(pi => pi.Name, pi => pi);foreach (var item in xml.Elements()){PropertyInfo propertyInfo;if (!dict.TryGetValue(item.Name.LocalName, out propertyInfo)) continue;var value = DeserializeValue(item, propertyInfo.PropertyType);propertyInfo.SetValue(result, value, null);}return result;}private object DeserializeArray(Type targetType){if (targetType.IsArray) // Foo[]{var elemType = targetType.GetElementType();dynamic array = Array.CreateInstance(elemType, xml.Elements().Count());var index = 0;foreach (var item in xml.Elements()){array[index++] = DeserializeValue(item, elemType);}return array;}else // List<Foo>{var elemType = targetType.GetGenericArguments()[0];dynamic list = Activator.CreateInstance(targetType);foreach (var item in xml.Elements()){list.Add(DeserializeValue(item, elemType));}return list;}}// Deletepublic override bool TryInvoke(InvokeBinder binder, object[] args, out object result){result = (IsArray)? Delete((int)args[0]): Delete((string)args[0]);return true;}// IsDefined, if has args then TryGetMemberpublic override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result){if (args.Length > 0){result = null;return false;}result = IsDefined(binder.Name);return true;}// Deserialize or foreach(IEnumerable)public override bool TryConvert(ConvertBinder binder, out object result){if (binder.Type == typeof(IEnumerable) || binder.Type == typeof(object[])){var ie = (IsArray)? xml.Elements().Select(x => ToValue(x)): xml.Elements().Select(x => (dynamic)new KeyValuePair<string, object>(x.Name.LocalName, ToValue(x)));result = (binder.Type == typeof(object[])) ? ie.ToArray() : ie;}else{result = Deserialize(binder.Type);}return true;}private bool TryGet(XElement element, out object result){if (element == null){result = null;return false;}result = ToValue(element);return true;}public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result){return (IsArray)? TryGet(xml.Elements().ElementAtOrDefault((int)indexes[0]), out result): TryGet(xml.Element((string)indexes[0]), out result);}public override bool TryGetMember(GetMemberBinder binder, out object result){return (IsArray)? TryGet(xml.Elements().ElementAtOrDefault(int.Parse(binder.Name)), out result): TryGet(xml.Element(binder.Name), out result);}private bool TrySet(string name, object value){var type = GetJsonType(value);var element = xml.Element(name);if (element == null){xml.Add(new XElement(name, CreateTypeAttr(type), CreateJsonNode(value)));}else{element.Attribute("type").Value = type.ToString();element.ReplaceNodes(CreateJsonNode(value));}return true;}private bool TrySet(int index, object value){var type = GetJsonType(value);var e = xml.Elements().ElementAtOrDefault(index);if (e == null){xml.Add(new XElement("item", CreateTypeAttr(type), CreateJsonNode(value)));}else{e.Attribute("type").Value = type.ToString();e.ReplaceNodes(CreateJsonNode(value));}return true;}public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value){return (IsArray)? TrySet((int)indexes[0], value): TrySet((string)indexes[0], value);}public override bool TrySetMember(SetMemberBinder binder, object value){return (IsArray)? TrySet(int.Parse(binder.Name), value): TrySet(binder.Name, value);}public override IEnumerable<string> GetDynamicMemberNames(){return (IsArray)? xml.Elements().Select((x, i) => i.ToString()): xml.Elements().Select(x => x.Name.LocalName);}/// <summary>Serialize to JsonString</summary>public override string ToString(){// <foo type="null"></foo> is can't serialize. replace to <foo type="null" />foreach (var elem in xml.Descendants().Where(x => x.Attribute("type").Value == "null")){elem.RemoveNodes();}return CreateJsonString(new XStreamingElement("root", CreateTypeAttr(jsonType), xml.Elements()));}}
}

有了这两大神器,基本上我的第一个目标:轻量级就可以实现了。

对API的包装就是按照官方文档一个一个来,主要是使用了简明的HttpClient。API一览:(Github截图)

API包装极简:

/*--------------------------------------------------------------------------
* BasicAPI.cs*Auth:deepleo
* Date:2013.12.31
* Email:2586662969@qq.com
<span style="font-family: Arial, Helvetica, sans-serif;">*更多: http://www.weixinsdk.net</span>
*--------------------------------------------------------------------------*/using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Http;
using Codeplex.Data;
using System.IO;namespace Deepleo.Weixin.SDK
{/// <summary>/// 对应微信API的 "基础支持"/// </summary>public class BasicAPI{/// <summary>/// 检查签名是否正确:/// http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97/// </summary>/// <param name="signature"></param>/// <param name="timestamp"></param>/// <param name="nonce"></param>/// <param name="token">AccessToken</param>/// <returns>/// true: check signature success/// false: check failed, 非微信官方调用!/// </returns>public static bool CheckSignature(string signature, string timestamp, string nonce, string token, out string ent){var arr = new[] { token, timestamp, nonce }.OrderBy(z => z).ToArray();var arrString = string.Join("", arr);var sha1 = System.Security.Cryptography.SHA1.Create();var sha1Arr = sha1.ComputeHash(Encoding.UTF8.GetBytes(arrString));StringBuilder enText = new StringBuilder();foreach (var b in sha1Arr){enText.AppendFormat("{0:x2}", b);}ent = enText.ToString();return signature == enText.ToString();}/// <summary>/// 获取AccessToken/// http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96access_token/// </summary>/// <param name="grant_type"></param>/// <param name="appid"></param>/// <param name="secrect"></param>/// <returns>access_toke</returns>public static dynamic GetAccessToken(string appid, string secrect){var url = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type={0}&appid={1}&secret={2}", "client_credential", appid, secrect);var client = new HttpClient();var result = client.GetAsync(url).Result;if (!result.IsSuccessStatusCode) return string.Empty;var token = DynamicJson.Parse(result.Content.ReadAsStringAsync().Result);return token;}/// <summary>/// 上传多媒体文件/// http://mp.weixin.qq.com/wiki/index.php?title=%E4%B8%8A%E4%BC%A0%E4%B8%8B%E8%BD%BD%E5%A4%9A%E5%AA%92%E4%BD%93%E6%96%87%E4%BB%B6/// 1.上传的媒体文件限制:///图片(image) : 1MB,支持JPG格式///语音(voice):1MB,播放长度不超过60s,支持MP4格式///视频(video):10MB,支持MP4格式///缩略图(thumb):64KB,支持JPG格式///2.媒体文件在后台保存时间为3天,即3天后media_id失效/// </summary>/// <param name="access_token"></param>/// <param name="type"></param>/// <param name="file"></param>/// <returns>media_id</returns>public static string UploadMedia(string access_token, string type, string file){var url = string.Format("http://api.weixin.qq.com/cgi-bin/media/upload?access_token={0}&type={1}&filename={2}", access_token, type, Path.GetFileName(file));var client = new HttpClient();var result = client.PostAsync(url, new StreamContent(new FileStream(file, FileMode.Open, FileAccess.Read)));if (!result.Result.IsSuccessStatusCode) return string.Empty;var media = DynamicJson.Parse(result.Result.Content.ReadAsStringAsync().Result);return media.media_id;}/// <summary>/// 下载多媒体/// 视频文件不支持下载,调用该接口需http协议。/// </summary>/// <param name="access_token"></param>/// <param name="media_id"></param>/// <returns></returns>public static bool DownloadMedia(string access_token, string media_id){var url = string.Format("http://file.api.weixin.qq.com/cgi-bin/media/get?access_token={0}&media_id={1}", access_token, media_id);var client = new HttpClient();var result = client.GetAsync(url);if (!result.Result.IsSuccessStatusCode) return false;return true;}}
}

完整源代码:Github地址:https://github.com/night-king/weixinSDK

微信公众平台SDK.net版本(weixinSDK.net)相关推荐

  1. 国内流行的两大开源.net微信公众平台SDK对比分析

    最近忙于微信周边的开发 难免手痒去搜索一下有没有相关的sdk直接拿来使 还真发现了不少 这里总结两个看起来比较不错的.net平台下基于C#语言开发的SDK 一个强大一个小巧 (1) Senparc.W ...

  2. 公众平台 python_微信公众平台SDK Python

    微信公众平台SDK 项目背景 从2014年开始玩微信公众平台,试用过其中大多数的功能,如:消息回复.自定义菜单.公众号中的支付,页面授权等.之前的程序中都是直接调用公众平台的接口,这样复用功能无法实现 ...

  3. 微信公众平台SDK JAVA实现WeChat-SDK

    前言 最近有做一些涉及到微信公众平台和第三方平台开发的工作.需要使用微信提供的接口.然而微信只提供了基于基本的HTTP接口,并没有对应各语言的SDK实现.所以如果自己开发的话需要封装一套SDK.不过, ...

  4. 微信公众平台SDK! Senparc.Weixin.MP.dll(资料整理)

    官网地址: http://weixin.senparc.com/ 使用 Senparc.Weixin.MP.dll 整合网站与微信公众账号的自动交流回复. 更多使用说明见:Senparc.Weixin ...

  5. 微信信息处理 微信公众平台开发教程(1)

     Senparc.Weixin SDK 是由盛派网络(Senparc)团队自主研发的针对微信各模块的 开发套件(C# SDK), 已全面支持微信公众号.微信支付.企业号.开放平台.JSSDK.摇一摇周 ...

  6. Senparc.Weixin.MP SDK 微信公众平台开发教程(十一):高级接口说明

    这里所说的高级接口是指面向通过认证的服务号开通的高级功能. 高级功能大致可以分类为: 用户接口 分组接口 客服接口(有别于之前介绍的多客服) 群发接口 多媒体接口 二维码接口 模板消息接口(不是所有账 ...

  7. Senparc.Weixin.MP SDK 微信公众平台开发教程(十八):Web代理功能

    在Senparc.Weixin.dll v4.5.7版本开始,我们提供了Web代理功能,以方便在受限制的局域网内的应用可以顺利调用接口. 有关的修改都在Senparc.Weixin/Utilities ...

  8. 最新微信公众平台js sdk整合PHP版

    由于没有持续关注微信公众平台相关的开发,所以看到这个东西时,都没有耐心看完开发文档,或者不知道重点. 重点在哪呢?重点在示例代码:http://mp.weixin.qq.com/wiki/7/aaa1 ...

  9. Senparc.Weixin.MP SDK 微信公众平台开发教程(二十二):如何安装 Nuget(dll) 后使用项目源代码调试...

    最近碰到开发者问:我使用 nuget 安装了 Senparc.Weixin SDK,但是有一些已经封装好的过程想要调试,我又不想直接附加源代码项目,这样就没有办法同步更新了,我应该怎么办? 这其实是一 ...

最新文章

  1. html在线缓存视频,javascript – 如何为HTML视频添加缓冲
  2. 转载 SharedPreference.Editor的apply和commit方法异同
  3. VTK修炼之道83:Pipeline管线执行模型
  4. 开发:异常收集之 ibatis+Oracle 查询时: ORA-00911错误
  5. 常见的10道Web前端面试题及答案分享!
  6. Android编译系统环境过程初始化分析【转】
  7. 内核kernel以及根文件系统rootfs是如何映射到对应的nand flash的
  8. GIS实战应用案例100篇(十六)-CASS道路横断面线,如何折线变直线?
  9. 流畅的python第一章_《流畅的Python》第一章学习笔记
  10. 让Jacob从当前路径读取dll文件及相关打包方法
  11. oracle监听管理工具,oracle监听器管理
  12. 微信个性状态来了,可以显示“等级”!
  13. 运动:延伸你的美丽(图)
  14. 左耳朵耗子:聊聊分布式系统架构
  15. Eclipse安装中文简体语言包(官方下载安装教程)
  16. win10下能够用的键盘映射工具? win10 下按键像mac一样
  17. TraceView工具如何使用
  18. Java Eclipse部分图标汇总
  19. 【大学物理·静止电荷的电场】静电场 电场强度
  20. Sleep()简析 和Sleep(0)的妙用

热门文章

  1. kony移动开发平台源码github地址
  2. 深度工作:充分使用每一份脑力--转载
  3. 在tableau server 上自定义报表
  4. oracle初始化参数详解
  5. 微服务架构中服务注册与发现
  6. JS逆向字体反爬,某供应商平台反爬实践
  7. LVM(logical volume manager) 逻辑卷管理器
  8. cf服务器断开连接不稳定,cf与服务器断开连接
  9. avast 免费好用的杀毒软件
  10. 软件测试基础面试常问问题(一)