钉钉开发系列(二)结构封装
钉钉的每个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";
}
}
至此,基本的结构封装已经完成。
欢迎打描左侧二维码打赏。
转载请注明出处。

浙公网安备 33010602011771号