钉钉的每个API接口返回的数据都包含有ErrCode和ErrMsg,由此我们想到可以使用基类来定义,之后的其他数据以继承的方式来达成。所以我们定义一个结果基类。
namespace DDSDK { public class ResultPackage { /// <summary> /// 错误码 /// </summary> public ErrCodeEnum ErrCode { get; set; } = ErrCodeEnum.Unknown; /// <summary> /// 错误消息 /// </summary> public string ErrMsg { get; set; } /// <summary> /// 结果的json形式 /// </summary> public String Json { get; set; } #region IsOK Function public bool IsOK() { return ErrCode == ErrCodeEnum.OK; } #endregion #region ToString public override string ToString() { String info = $"{nameof(ErrCode)}:{ErrCode},{nameof(ErrMsg)}:{ErrMsg}"; return info; } #endregion } }为了便于查验返回的结果,我们又定义了一个Json来保存取回的数据。为了能更好的判断数据包是否正确,增加了IsOK函数。为了更好的查看错误码和错误信息,我们重载了ToString的方法。
有了结果的基类,那么我们就可以将前一篇的获取access_token的结果类重新定义,代码如下
public class TokenResult:ResultPackage { public string Access_token { get; set; } }由于我们每次发起的都是网络请求,而网络请求主要是以GET和POST的方式发起的,所以我们可以定义一个分析器,在分析器中封装GET和POST的请求方法,之后将相应的结果以泛型的形式返回。代码如下。
namespace DDSDK { /// <summary> /// 分析器 /// </summary> public class Analyze { #region Get Function /// <summary> /// 发起GET请求 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="requestUrl"></param> /// <returns></returns> public static T Get<T>(String requestUrl) where T : ResultPackage, new() { String resultJson = RequestHelper.Get(requestUrl); return AnalyzeResult<T>(resultJson); } #endregion #region Post Function /// <summary> /// 发起POST请求 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="requestUrl"></param> /// <param name="requestParamOfJsonStr"></param> /// <returns></returns> public static T Post<T>(String requestUrl, String requestParamOfJsonStr) where T : ResultPackage, new() { String resultJson = RequestHelper.Post(requestUrl, requestParamOfJsonStr); return AnalyzeResult<T>(resultJson); } #endregion #region AnalyzeResult /// <summary> /// 分析结果 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="resultJson"></param> /// <returns></returns> private static T AnalyzeResult<T>(string resultJson) where T : ResultPackage, new() { ResultPackage tempResult = null; if (!String.IsNullOrEmpty(resultJson)) { tempResult = JsonConvert.DeserializeObject<ResultPackage>(resultJson); } T result = null; if (tempResult != null && tempResult.IsOK()) { result = JsonConvert.DeserializeObject<T>(resultJson); } else if (tempResult != null) { result = tempResult as T; } else if (tempResult == null) { result = new T(); } result.Json = resultJson; return result; } #endregion } }为了能让泛型类保持结果的基本一致性,我们将泛型类作了限定,即where T : ResultPackage, new(),这句话的意思是说泛型T必须源于ResultPackage,且要实现无参的构造方法。为了保证结果的正确返回,我们在得到返回的json数据串时,先序列化基本类,判断结果是否OK,然后再二次序列化成最终的结果类。
下面是RequestHelper类
namespace DDSDK { /// <summary> /// 请求协助类 /// </summary> public class RequestHelper { #region Get /// <summary> /// 执行基本的命令方法,以Get方式 /// </summary> /// <param name="apiurl"></param> /// <returns></returns> public static String Get(string apiurl) { WebRequest request = WebRequest.Create(@apiurl); request.Method = "GET"; WebResponse response = request.GetResponse(); Stream stream = response.GetResponseStream(); Encoding encode = Encoding.UTF8; StreamReader reader = new StreamReader(stream, encode); string resultJson = reader.ReadToEnd(); return resultJson; } #endregion #region Post /// <summary> /// 以Post方式提交命令 /// </summary> public static String Post(string apiurl, string jsonString) { WebRequest request = WebRequest.Create(@apiurl); request.Method = "POST"; request.ContentType = "application/json"; byte[] bs = Encoding.UTF8.GetBytes(jsonString); request.ContentLength = bs.Length; Stream newStream = request.GetRequestStream(); newStream.Write(bs, 0, bs.Length); newStream.Close(); WebResponse response = request.GetResponse(); Stream stream = response.GetResponseStream(); Encoding encode = Encoding.UTF8; StreamReader reader = new StreamReader(stream, encode); string resultJson = reader.ReadToEnd(); return resultJson; } #endregion } }有了这些封装后,我们前一篇中获取access_token的方法就可进一步简化,简化代码如下
#region UpdateAccessToken /// <summary> ///更新票据 /// </summary> /// <param name="forced">true:强制更新.false:按缓存是否到期来更新</param> public static void UpdateAccessToken(bool forced = false) { if (!forced && AccessToken.Begin.AddSeconds(ConstVars.CACHE_TIME) >= DateTime.Now) {//没有强制更新,并且没有超过缓存时间 return; } string CorpID = ConfigHelper.FetchCorpID(); string CorpSecret = ConfigHelper.FetchCorpSecret(); string TokenUrl = Urls.gettoken; string apiurl = $"{TokenUrl}?{Keys.corpid}={CorpID}&{Keys.corpsecret}={CorpSecret}"; TokenResult tokenResult = Analyze.Get<TokenResult>(apiurl); if (tokenResult.ErrCode== ErrCodeEnum.OK) { AccessToken.Value = tokenResult.Access_token; AccessToken.Begin = DateTime.Now; } } #endregion在发起请求的过程中,会用到很多的常量和KEY,这里专门定义了相关的类来保存,以方便需要的地方调用。
namespace DDSDK { public class ConstVars { /// <summary> /// 缓存的JS票据的KEY /// </summary> public const string CACHE_JS_TICKET_KEY = "CACHE_JS_TICKET_KEY"; /// <summary> /// 缓存时间 /// </summary> public const int CACHE_TIME = 5000; } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DDSDK { /// <summary> /// Url的Key /// </summary> public sealed class Keys { public const string corpid = "corpid"; public const string corpsecret = "corpsecret"; public const string department_id = "department_id"; public const string userid = "userid"; public const string chatid = "chatid"; public const string access_token = "access_token"; public const string jsapi_ticket = "jsapi_ticket"; public const string noncestr = "noncestr"; public const string timestamp = "timestamp"; public const string url = "url"; } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DDSDK { /// <summary> /// SDK使用的URL /// </summary> public sealed class Urls { /// <summary> /// 创建会话 /// </summary> public const string chat_create = "https://oapi.dingtalk.com/chat/create"; /// <summary> /// 获取会话信息 /// </summary> public const string chat_get = "https://oapi.dingtalk.com/chat/get"; /// <summary> /// 发送会话消息 /// </summary> public const string chat_send = "https://oapi.dingtalk.com/chat/send"; /// <summary> /// 更新会话信息 /// </summary> public const string chat_update = "https://oapi.dingtalk.com/chat/update"; /// <summary> /// 获取部门列表 /// </summary> public const string department_list = "https://oapi.dingtalk.com/department/list"; /// <summary> /// 获取访问票记 /// </summary> public const string gettoken = "https://oapi.dingtalk.com/gettoken"; /// <summary> /// 发送消息 /// </summary> public const string message_send = "https://oapi.dingtalk.com/message/send"; /// <summary> /// 用户列表 /// </summary> public const string user_list = "https://oapi.dingtalk.com/user/list"; /// <summary> /// 用户详情 /// </summary> public const string user_get = "https://oapi.dingtalk.com/user/get"; /// <summary> /// 获取JSAPI的票据 /// </summary> public const string get_jsapi_ticket = "https://oapi.dingtalk.com/get_jsapi_ticket"; } }
至此,基本的结构封装已经完成。
欢迎打描左侧二维码打赏。
转载请注明出处。