ABP框架能夠動態生成WebApi,開發人員無需建立APIController,直接繼承IApplicationService接口,便可對外發布webApi。web
建立動態Web Api 控制器api
例如,在Service層有一個ITestService接口,以下:瀏覽器
public interface ITestService : IApplicationService { List<int> GetTestMethod(); string GetAll(); string GetById(int id); }
該接口實現了「IApplicationService」接口,在該框架中,咱們無需構建TestServiceApiController就能夠對外發布webapi。用戶能夠直接經過訪問「api/services/app/TestService/GetTestMethod」實現api 訪問。app
實現原理:框架
模塊初始化階段,註冊動態API實現模塊,在批量/單個註冊方法中,執行「BatchApiControllerBuilder」的「Build」方法,遍歷Application層程序集,查找全部已註冊接口類型。而後根據類型信息獲取服務名稱,生成單個「ApiControllerBuilder」實例,依次執行「ApiControllerBuilder」中的方法。其中生成「action」是在「Builder」中實現的。ide
在「ApiControllerBuilder」的「Builder」方法中」,經過「Build」方法構建apiinfo信息並將「action」添加到controller中,最後在apicontroller管理類中註冊controller信息。函數
如下對類和接口逐一分析ui
AbpApiController:集成了ApiController,框架中自定義的apicontroller都繼承自該類;this
IDynamicApiController:空接口,生成DynamicApiController標識;spa
DynamicApiController<T>:動態生成ApiController類,繼承自「AbpApiController」,「IDynamicApiController」;
1 public class DynamicApiController<T>: AbpApiController, IDynamicApiController 2 { 3 public List<string> AppliedCrossCuttingConcerns { get; } 4 public DynamicApiController() 5 { 6 AppliedCrossCuttingConcerns = new List<string>(); 7 } 8 }
DynamicApiControllerInfo:封裝ApiController基本信息,其中以字典的形式存儲了「DynamicApiActionInfo」;
1 public DynamicApiControllerInfo( 2 string serviceName, 3 Type serviceInterfaceType, 4 Type apiControllerType, 5 Type interceptorType, 6 IFilter[] filters = null, 7 bool? isApiExplorerEnabled = null, 8 bool isProxyScriptingEnabled = true) 9 { 10 ServiceName = serviceName; 11 ServiceInterfaceType = serviceInterfaceType; 12 ApiControllerType = apiControllerType; 13 InterceptorType = interceptorType; 14 IsApiExplorerEnabled = isApiExplorerEnabled; 15 IsProxyScriptingEnabled = isProxyScriptingEnabled; 16 Filters = filters ?? new IFilter[] { }; //Assigning or initialzing the action filters. 17 Actions = new Dictionary<string, DynamicApiActionInfo>(StringComparer.InvariantCultureIgnoreCase); 18 }
IBatchApiControllerBuilder<T>/BatchApiControllerBuilder<T>:批量ApiController構建器,經過「Build」方法,根據程序集,批量生成「DynamicApiControllerInfo」;
internal class BatchApiControllerBuilder<T> : IBatchApiControllerBuilder<T> { #region 聲明實例 private readonly string _servicePrefix; private readonly Assembly _assembly; private IFilter[] _filters; private Func<Type, string> _serviceNameSelector; private Func<Type, bool> _typePredicate; private bool _conventionalVerbs; private Action<IApiControllerActionBuilder<T>> _forMethodsAction; private bool? _isApiExplorerEnabled; private readonly IIocResolver _iocResolver; private readonly IDynamicApiControllerBuilder _dynamicApiControllerBuilder; private bool? _isProxyScriptingEnabled; #endregion #region 構造函數 public BatchApiControllerBuilder( IIocResolver iocResolver, IDynamicApiControllerBuilder dynamicApiControllerBuilder, Assembly assembly, string servicePrefix) { _iocResolver = iocResolver; _dynamicApiControllerBuilder = dynamicApiControllerBuilder; _assembly = assembly; _servicePrefix = servicePrefix; } #endregion #region 方法 public void Build() { var types = from type in _assembly.GetTypes() where (type.IsPublic || type.IsNestedPublic) && type.IsInterface && typeof(T).IsAssignableFrom(type) && _iocResolver.IsRegistered(type) && !RemoteServiceAttribute.IsExplicitlyDisabledFor(type) select type; if (_typePredicate != null) { types = types.Where(t => _typePredicate(t)); } foreach (var type in types) { var serviceName=_serviceNameSelector!=null? _serviceNameSelector(type) : GetConventionalServiceName(type); if (!string.IsNullOrWhiteSpace(_servicePrefix)) { serviceName = _servicePrefix + "/" + serviceName; } var builder = typeof(IDynamicApiControllerBuilder) .GetMethod("For", BindingFlags.Public | BindingFlags.Instance) .MakeGenericMethod(type) .Invoke(_dynamicApiControllerBuilder, new object[] { serviceName }); if (_filters != null) { builder.GetType() .GetMethod("WithFilters", BindingFlags.Public | BindingFlags.Instance) .Invoke(builder, new object[] { _filters }); } if (_isApiExplorerEnabled != null) { builder.GetType() .GetMethod("WithApiExplorer", BindingFlags.Public | BindingFlags.Instance) .Invoke(builder, new object[] { _isApiExplorerEnabled }); } if (_isProxyScriptingEnabled != null) { builder.GetType() .GetMethod("WithProxyScripts", BindingFlags.Public | BindingFlags.Instance) .Invoke(builder, new object[] { _isProxyScriptingEnabled.Value }); } if (_conventionalVerbs) { builder.GetType() .GetMethod("WithConventionalVerbs", BindingFlags.Public | BindingFlags.Instance) .Invoke(builder, new object[0]); } if (_forMethodsAction != null) { builder.GetType() .GetMethod("ForMethods", BindingFlags.Public | BindingFlags.Instance) .Invoke(builder, new object[] { _forMethodsAction }); } builder.GetType() .GetMethod("Build", BindingFlags.Public | BindingFlags.Instance) .Invoke(builder, new object[0]); } } private string GetConventionalServiceName(Type type) { var typeName = type.Name; typeName = typeName.RemovePostFix(ApplicationService.CommonPostfixes); if (typeName.Length > 1 && typeName.StartsWith("I") && char.IsUpper(typeName, 1)) { typeName = typeName.Substring(1); } return typeName.ToCamelCase(); } public IBatchApiControllerBuilder<T> ForMethods(Action<IApiControllerActionBuilder> action) { _forMethodsAction = action; return this; } public IBatchApiControllerBuilder<T> Where(Func<Type, bool> predicate) { _typePredicate = predicate; return this; } public IBatchApiControllerBuilder<T> WithApiExplorer(bool isEnabled) { _isApiExplorerEnabled = isEnabled; return this; } public IBatchApiControllerBuilder<T> WithConventionalVerbs() { _conventionalVerbs = true; return this; } public IBatchApiControllerBuilder<T> WithFilters(params IFilter[] filters) { _filters = filters; return this; } public IBatchApiControllerBuilder<T> WithProxyScripts(bool isEnabled) { _isProxyScriptingEnabled = isEnabled; return this; } public IBatchApiControllerBuilder<T> WithServiceName(Func<Type, string> serviceNameSelector) { _serviceNameSelector = serviceNameSelector; return this; } #endregion }
DynamicApiControllerBuilder/IDynamicApiControllerBuilder:動態ApiController構建器。「For」方法構建了「ApiControllerBuilder」實例;「ForAll」生成了「BatchApiControllerBuilder」,用於批量生成「DynamicApiControllerInfo」實例;
IApiControllerBuilder/ApiControllerBuilder:單個ApiController構建器,每一個ABPApiCon以troller中存儲了「Action」的基本信息。經過「Build」方法,生成「DynamicApiControllerInfo」實例,而後遍歷「Action」,添加到「IDictionary<string, ApiControllerActionBuilder<T>>」字典中;
1 public void Build() 2 { 3 var controllerInfo = new DynamicApiControllerInfo( 4 ServiceName, 5 ServiceInterfaceType, 6 typeof(DynamicApiController<T>), 7 typeof(AbpDynamicApiControllerInterceptor<T>), 8 Filters, 9 IsApiExplorerEnabled, 10 IsProxyScriptingEnabled 11 ); 12 foreach (var actionBuilder in _actionBuilders.Values) 13 { 14 if (actionBuilder.DontCreate) 15 { 16 continue; 17 } 18 controllerInfo.Actions[actionBuilder.ActionName] = actionBuilder.BuildActionInfo(ConventionalVerbs); 19 20 } 21 _iocResolver.Resolve<DynamicApiControllerManager>().Register(controllerInfo); 22 }
DynamicApiActionInfo:封裝了「Action」名稱、請求方式等基本信息;
1 /// <summary> 2 /// 封裝動態生成的ApiController的Action的信息 3 /// </summary> 4 public class DynamicApiActionInfo 5 { 6 /// <summary> 7 /// action 名稱 8 /// </summary> 9 public string ActionName { get; private set; } 10 /// <summary> 11 /// 方法信息 12 /// </summary> 13 public MethodInfo Method { get; private set; } 14 public HttpVerb Verb { get; private set; } 15 /// <summary> 16 /// 過濾器 17 /// </summary> 18 public IFilter[] Filters { get; set; } 19 /// <summary> 20 /// Is API Explorer enabled. 21 /// </summary> 22 public bool? IsApiExplorerEnabled { get; set; } 23 /// <summary> 24 /// 構造函數 25 /// </summary> 26 /// <param name="actionName"></param> 27 /// <param name="verb"></param> 28 /// <param name="method"></param> 29 /// <param name="filters"></param> 30 /// <param name="isApiExplorerEnabled"></param> 31 public DynamicApiActionInfo( 32 string actionName, 33 HttpVerb verb, 34 MethodInfo method, 35 IFilter[] filters = null, 36 bool? isApiExplorerEnabled = null) 37 { 38 ActionName = actionName; 39 Verb = verb; 40 Method = method; 41 IsApiExplorerEnabled = isApiExplorerEnabled; 42 Filters = filters ?? new IFilter[] { }; //Assigning or initialzing the action filters. 43 } 44 }
IApiControllerActionBuilder/ApiControllerActionBuilder:「ApiActionController」構建器,生成「DynamicApiActionInfo」對象;
DynamicApiControllerManager:ApiController管理類,以字典的形式,管理控制器。當瀏覽器接受到「HttpRouteData」請求時,程序根據服務的名稱從該類中查找相應的controller;
DynamicHttpControllerDescriptor:繼承自「HttpControllerDescriptor」;
AbpHttpControllerSelector:繼承自「DefaultHttpControllerSelector」,重寫了「SelectController」方法,返回新的「HttpControllerDescriptor」。在該類中,根據路由信息中的服務類名稱,查找制定的「DynamicApiControllerInfo」;
AbpApiControllerActionSelector:繼承自ASP.Net WebAPI 的 ApiControllerActionSelector,AbpApiControllerActionSelector 經過調用DynamicApiServiceNameHelper的靜態方法(傳入routedata中的serviceNameWithAction)獲取action實例;
AbpApiControllerActivator :實現了 IHttpControllerActivator接口,根據controller的類型生成指定的controller;
AbpDynamicApiControllerInterceptor<T> :方法攔截器,攔截「Action」請求,調用服務層中的方法。
try { invocation.ReturnValue=invocation.Method.Invoke(_proxiedObject, invocation.Arguments); } catch (TargetInvocationException targetInvocation) { if (targetInvocation.InnerException != null) { targetInvocation.InnerException.ReThrow(); } throw; }
攔截器在模塊的初始化階段註冊:
1 public override void PostInitialize() 2 { 3 var httpConfiguration= IocManager.Resolve<IAbpWebApiConfiguration>().HttpConfiguration; 4 InitializeRoutes(httpConfiguration); 5 InitializeAspNetServices(httpConfiguration); 6 7 foreach (var controllerInfo in IocManager.Resolve<DynamicApiControllerManager>().GetAll()) 8 { 9 IocManager.IocContainer.Register( 10 Component.For(controllerInfo.InterceptorType).LifestyleTransient(), 11 Component.For(controllerInfo.ApiControllerType) 12 .Proxy.AdditionalInterfaces(controllerInfo.ServiceInterfaceType) 13 .Interceptors(controllerInfo.InterceptorType) 14 .LifestyleTransient() 15 ); 16 17 //LogHelper.Logger.DebugFormat("Dynamic web api controller is created for type '{0}' with service name '{1}'.", controllerInfo.ServiceInterfaceType.FullName, controllerInfo.ServiceName); 18 } 19 20 Configuration.Modules.AbpWebApi().HttpConfiguration.EnsureInitialized(); 21 //base.PostInitialize(); 22 }