钉钉开发系列(二)结构封装

钉钉的每个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";
    }
}

至此,基本的结构封装已经完成。

欢迎打描左侧二维码打赏。

转载请注明出处。







原文地址:https://www.cnblogs.com/sparkleDai/p/7604933.html