小弟之前有分享过串接【财政部查询类电子发票API】小框架使用文
使用文连结 快速使用财政部电子发票API 使用 C#
串接文件下载 电子发票查询API 1.4.4
程式原始码连结
这次想跟大家分享我做出此框架的历程..
框架目的:希望可以做出方便日后维护扩展的API。
串接API时发现他们有几个共同之处
API请求参数名称需降幂排列请求参数最后会加上签章都有时间戳记回应资料格式都是Json都是使用Http (Get or Post)我就想到可以使用 工厂模式来实作这系列产品 (工厂模式主要是切割产品的使用和生产)
(一) 产品解说
因为他们都有共同的能力**传入一组参数回传一串Json **
我就先写出一个API共同的介面签章 IApiRunner
这个介面为基础来撰写后面的程式码
/// <summary>/// 执行api的介面/// </summary>public interface IApiRunner{ /// <summary> /// 执行api /// </summary> /// <param name="model">传入的参数</param> /// <returns>回传资料</returns> string ExcuteApi(object model);}
我在中间多一个抽象泛型类别 ApiBase (用泛型是为了给子类决定传入参数的Model)
原因:
/// <summary>/// 子类继承提供参数/// </summary>/// <returns></returns>protected abstract string SetParamter(T model);/// <summary>/// 取得api的Url路径/// </summary>/// <returns></returns>protected virtual string GetApiURL(){ string apiname = this.GetType().Name; if (!ConfigurationManager.AppSettings.AllKeys.Contains(apiname)) { throw new Exception(string.Format("请确认Config的appsetting有无此参数 {0}", apiname)); } return ConfigurationManager.AppSettings[apiname];}/// <summary>/// 执行Api/// </summary>/// <param name="model"></param>/// <returns></returns>public virtual string ExcuteApi(object model){ //建立所需参数 string result = string.Empty; string postData = string.Empty; string posturl = GetApiURL(); var data = ObjectToModel(model); //取得加密后的参数 postData = GetInvoiceParamter(SetParamter(data)); try { ServicePointManager.ServerCertificateValidationCallback = HttpTool.ValidateServerCertificate; result = HttpTool.HttpPost(posturl, postData); } catch (Exception ex) { result = GetSysErrorMsg(); } return result;}
目前产品部分已经建构好了。
(二) 工厂解说
工厂部分这次我选择使用【反射方式来实现工厂】
工厂类别 MoblieInvoiceApiFactroy
其实最主要是使用GetInstance方法
/// <summary>/// 提供api的工厂/// Model和Api命名要相关/// 例如:testModel 对 testApi/// </summary>/// <param name="model">Model参数</param>/// <returns></returns>public static IApiRunner GetInstace(object model){ if (model == null) throw new ArgumentNullException("不能传空的参数"); string modelName = model.GetType().Name; return (IApiRunner)Activator.CreateInstance (GetInstanceType(model), null);}
其中我把决定使用哪个组API的决定权交给Model并写在标籤上(Attirbute)
在执行时他可获取此参数Model所注册参数的型别,来动态产生产品
/// <summary>/// 反射取得绑定Model上绑定的API型别/// </summary>/// <param name="model"></param>/// <returns></returns>public static Type GetInstanceType(object model){ var modelType = model.GetType(); var attr = modelType.GetCustomAttribute(typeof(ApiTypeAttribute)) as ApiTypeAttribute; if (attr != null) { return GetApiType(attr); } throw new Exception("Model尚未赋予ApiTypeAttribute");}
EX:查询中奖号码API Model 可以很清楚知道这个Model隶属于哪个API
[ApiType(ApiType = typeof(QryWinningListApi), MockApiType = typeof(QryWinningListMockApi))]public class QryWinningListModel{ public string invTerm { get; set; }}
值得一提的是它有多一个MockApiType 为什么会有这个?
原因:如果财政部伺服器连不到我们可以改成假资料或是模拟资料(读取资料库或是其他方式)。
以上就是此框架的解说
外部只需要呼叫工厂的GetInstance方法并传入参数Model就会回传相对应的产品API类别
这样就降低执行和产生产品的耦合度,因为外部不是直接强耦合于Api类别而是透过工厂
日后如需增加API产品只需扩展新的类别 符合OCP(开放封闭原则)
已经达到目的:希望可以做出方便日后维护扩展的API。
Ps:这次我除了使用 工厂模式,也有用到样板模式,代理模式 剩下两个模式让大家来找看看吧^^
一般来说很少只用一个模式就可以解决一个问题的,通常都是配合使用