利用委托与Lambada创建和调用webapi接口
前言
现在项目中用的是webapi,其中有以下问题:
1.接口随着开发的增多逐渐增加相当庞大。
2.接口调用时不好管理。
以上是主要问题,对此就衍生了一个想法:
如果每一个接口都一个配置文件来管理,每个配置文件能清晰表示处理接口文件,地址,参数,返回值,那么通过这个配置文件,就能很好的管理起来我们所有的webapi接口不是吗?
有了这个思路之后就有了以下的实现:
1.具体实现:
public void Build_Apis() { foreach (var ass in AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetCustomAttributes(typeof(QuickWebApiDllAttribute), true).Length > 0)) { var vatt = ass.GetCustomAttribute<AssemblyFileVersionAttribute>(); var tatt = ass.GetCustomAttribute<AssemblyTitleAttribute>(); var datt = ass.GetCustomAttribute<QuickWebApiDllAttribute>(); apis.Clear(); var input_types = new List<Type>(); foreach (var type in ass.GetTypes()) { var attr = type.GetCustomAttribute<QuickWebApiAttribute>(); if (attr != null) { WebApiNode api = new WebApiNode(datt.Domain) { Name = attr.name, Service = attr.service, Route = attr.route, Comment = attr.comment, Version = vatt.Version, Title = tatt.Title }; foreach (var mi in type.GetMethods()) { var att = mi.GetCustomAttribute<QuickWebApiAttribute>(); if (att != null) { var act = new WebApiMethod() { Action = mi.Name, Code = att.service, Method = att.methodtype, Name = string.IsNullOrWhiteSpace(att.name) ? mi.Name : att.name, Comment = att.comment, OutputType = att.resultype }; foreach (var arg in mi.GetParameters()) { var mdatt = arg.ParameterType.GetCustomAttribute<DescriptionAttribute>(); act.Params.Add(new WebApiMethodParam() { Name = arg.Name, TypeName = arg.ParameterType.Name, DefaultValue = string.IsNullOrWhiteSpace(arg.DefaultValue.ToString()) ? "无默认值" : arg.DefaultValue.ToString(), Desc = mdatt == null ? "" : mdatt.Description }); if (arg.ParameterType.IsClass && arg.ParameterType != typeof(string)) { if (!input_types.Exists(t => t.Name == arg.ParameterType.Name)) input_types.Add(arg.ParameterType); } } if (!api.Methods.Exists(a => a.Action == act.Action)) api.Methods.Add(act); if (att.resultype != null && att.resultype.IsClass && att.resultype != typeof(string)) { if (!input_types.Exists(t => t.Name == att.resultype.Name)) input_types.Add(att.resultype); } } } if (!apis.Exists(a => a.Service == api.Service)) apis.Add(api); } } Build_Apids_Config(apis, datt.Name); Build_Apids_Doc(apis, datt.Name, input_types); } }
1.1:其中Build_Apis()方法,是系统根据,webapi接口描述,创建的对应的接口服务配置文件操作。
配置如下:
生成接口文件如下:
接口的实现
接下来只需你把xml文件引用到你要调用的站点下即可。
public string Load_Apis() { var files = System.IO.Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "apis_*.xml", System.IO.SearchOption.AllDirectories); apis.Clear(); foreach (var path in files) { var jss = System.IO.File.ReadAllText(path); var _apis = Deserialize<List<WebApiNode>>(jss); if (_apis == null || _apis.Count == 0) continue; foreach (var api in _apis) { if (apis.Exists(a => a.Service == api.Service)) continue; apis.Add(api); } } return string.Format("service:{0}, action:{1}", apis.Count, apis.Sum(a => a.Methods.Count)); }
1.2:其中Load_Apis()方式是在程序启动时加载webapi服务配置文件的操作,在这不再累述。
public class webapi<T, tresp> where tresp : class,new() { public webapi() { } public webapi(string service_prefix) { _service_prefix = service_prefix; } public webapi(long service_prefix_id) { _service_prefix = service_prefix_id.ToString(); } protected string build_server(string srv) { return string.IsNullOrWhiteSpace(_service_prefix) ? srv : string.Format("{0}_{1}", _service_prefix, srv); } string _service_prefix; public result<tresp> invoke(Expression<Func<T, apiaction_l>> func, long args1) { return _invoke(func.Body, args1); } public result<tresp> invoke(Expression<Func<T, apiaction_ll>> func, long args1, long args2) { return _invoke(func.Body, args1, args2); } public result<tresp> invoke(Expression<Func<T, apiaction_li>> func, long args1, int args2) { return _invoke(func.Body, args1, args2); } public result<tresp> invoke(Expression<Func<T, apiaction_ls>> func, long args1, string args2) { return _invoke(func.Body, args1, args2); } //public result<tresp> invoke<treq>(Expression<Func<T, apiaction_s<treq>>> func, treq args1) where treq : struct //{ // return _invoke(func.Body, args1); //} public result<tresp> invoke(Expression<Func<T, apiaction_i>> func, int args1) { return _invoke(func.Body, args1); } public result<tresp> invoke(Expression<Func<T, apiaction_ii>> func, int args1, int args2) { return _invoke(func.Body, args1, args2); } public result<tresp> invoke(Expression<Func<T, apiaction_il>> func, int args1, long args2) { return _invoke(func.Body, args1, args2); } public result<tresp> invoke(Expression<Func<T, apiaction_is>> func, int args1, string args2) { return _invoke(func.Body, args1, args2); } public result<tresp> invoke(Expression<Func<T, apiaction_ss>> func, string args1, string args2) { return _invoke(func.Body, args1, args2); } public result<tresp> invoke(Expression<Func<T, apiaction_sss>> func, string args1, string args2, string args3) { return _invoke(func.Body, args1, args2, args3); } public result<tresp> invoke<treq>(Expression<Func<T, apiaction_o<treq>>> func, treq data) where treq : class,new() { if (data != null && data is String) { return _invoke(func.Body, data); } return _invoke_data<treq>(func.Body, data); } public result<tresp> invoke(Expression<Func<T, apiaction>> func) { return _invoke_data<object>(func.Body, null); } result<tresp> _invoke_data<treq>(Expression exp, treq data) where treq : class { var method = ((exp as UnaryExpression).Operand as MethodCallExpression); string code = ((method.Object as ConstantExpression).Value as MethodInfo).Name; foreach (var m in method.Arguments) { if (m.Type == typeof(T)) { var attr = m.Type.GetCustomAttribute<QuickWebApiAttribute>(); if (attr != null) { return new invoker(build_server(attr.service)).Excute<tresp>(code, data); } } } return new result<tresp>(-1, "未能找到合适的api定义"); } result<tresp> _invoke(Expression exp, params object[] args) { var method = ((exp as UnaryExpression).Operand as MethodCallExpression); string code = ((method.Object as ConstantExpression).Value as MethodInfo).Name; foreach (var m in method.Arguments) { if (m.Type == typeof(T)) { var attr = m.Type.GetCustomAttribute<QuickWebApiAttribute>(); StringBuilder sb = new StringBuilder(); var pis = m.Type.GetMethod(code).GetParameters(); for (int i = 0; i < pis.Length; i++) { sb.AppendFormat("{0}={1}&", pis[i].Name, args[i] is DateTime ? ((DateTime)args[i]).ToString("yyyy-MM-dd HH:mm:ss") : args[i]); } if (attr != null) { return new invoker(build_server(attr.service)).Excute<tresp>(code, sb.ToString()); } } } return new result<tresp>(-1, "未能找到合适的api定义"); } }
1.3:其中 result<tresp> _invoke是:通过lambda对传递过来的委托,进行相应的反射操作。
其中委托定义如下:
public delegate IHttpActionResult apiaction(); public delegate IHttpActionResult apiaction_l(long args); public delegate IHttpActionResult apiaction_ll(long args1, long args2); public delegate IHttpActionResult apiaction_li(long args1, int arg2); public delegate IHttpActionResult apiaction_ls(long args1, string args2); public delegate IHttpActionResult apiaction_i(int args1); public delegate IHttpActionResult apiaction_ii(int args1, int args2); public delegate IHttpActionResult apiaction_is(int args1, string args2); public delegate IHttpActionResult apiaction_il(int args1, long args2); public delegate IHttpActionResult apiaction_si(string args1, int args2); public delegate IHttpActionResult apiaction_ss(string args1, string args2); public delegate IHttpActionResult apiaction_sl(string args1, long args2); public delegate IHttpActionResult apiaction_sss(string args1, string args2, string args3); public delegate IHttpActionResult apiaction_o<treq>(treq data) where treq : class,new();
注:目前delegate只支持三个参数以内的接口(且参数类型目前仅支持int,long,string),如果参数不符合条件可传递对象。
以上为具体实现至于有不了解的可以在文章顶部下载代码也可以,点击公共中qq与我联系。
2.下面我来写一下它的使用:
2.1.初始化:
在global中添加如下代码: