ABP的動態WebApi實現了直接對服務層的調用(其實病沒有跨過ApiController,只是將ApiController公共化,對於這一點的處理相似於MVC,對服務端的 調用沒有跨過HttpHandler同樣),這樣不只減小了ApiController的開發,也更能體現驅動領域設計的層結構。web
AbpWebApiModule是Abp.Web.Api的模塊類,該類中定義InitializeAspNetServices,InitializeRoutes兩個方法,而且在模塊的Initialize方法中執行,這兩個方法分別是對WebApi的服務的替換與路由的配置,。這兩處對WebApi的變動才使得直接調用服務層成爲可能。 api
private static void InitializeAspNetServices() { GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector), new AbpHttpControllerSelector(GlobalConfiguration.Configuration)); GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpActionSelector), new AbpApiControllerActionSelector()); GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new AbpControllerActivator()); } private static void InitializeRoutes() { DynamicApiRouteConfig.Register(); } public static void Register() { //Dynamic Web APIs (with area name) GlobalConfiguration.Configuration.Routes.MapHttpRoute( name: "AbpDynamicWebApi", routeTemplate: "api/services/{*serviceNameWithAction}" ); }
再對服務信息的存儲上,做者提供了DynamicApiControllerInfo,DynamicApiActionInfo(源碼中的DynamicApiMethodInfo.cs),其中DynamicApiControllerInfo包含了一DynamicApiActionInfo集合。 緩存
internal class DynamicApiControllerInfo { /// <summary> /// Name of the service. /// </summary> public string ServiceName { get; private set; } /// <summary> /// Controller type. /// </summary> public Type Type { get; private set; } /// <summary> /// Dynamic Action Filters for this controller. /// </summary> public IFilter[] Filters { get; set; } /// <summary> /// All actions of the controller. /// </summary> public IDictionary<string, DynamicApiActionInfo> Actions { get; private set; } /// <summary> /// Creates a new <see cref="DynamicApiControllerInfo"/> instance. /// </summary> /// <param name="serviceName">Name of the service</param> /// <param name="type">Controller type</param> /// <param name="filters">Filters</param> public DynamicApiControllerInfo(string serviceName, Type type, IFilter[] filters = null) { ServiceName = serviceName; Type = type; Filters = filters ?? new IFilter[] { }; //Assigning or initialzing the action filters. Actions = new Dictionary<string, DynamicApiActionInfo>(StringComparer.InvariantCultureIgnoreCase); } }
在執行AbpHttpControllerSelector, AbpApiControllerActionSelector, AbpControllerActivator的時候,系統已經在初始化的時候對服務層進行了分析與緩存。 app
在做者給的Demo SimpleTaskSystem下有一模塊類SimpleTaskSystemWebApiModule ide
[DependsOn(typeof(AbpWebApiModule))] //We declare depended modules explicitly public class SimpleTaskSystemWebApiModule : AbpModule { public override void Initialize() { //This code is used to register classes to dependency injection system for this assembly using conventions. IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly()); //Creating dynamic Web Api Controllers for application services. //Thus, 'web api layer' is created automatically by ABP. DynamicApiControllerBuilder .ForAll<IApplicationService>(Assembly.GetAssembly(typeof (SimpleTaskSystemApplicationModule)), "tasksystem") .Build(); } }
在這裏是使用到了DynamicApiControllerBuilder,這個類主要是對服務接口進行一個註冊,再由IBatchApiControllerBuilder按照註冊的服務接口對提供的程序集進行分析。 ui
DynamicApiControllerBuilder提供的ForAll只是返回的一個IBatchApiControllerBuilder實現對象 this
public static IBatchApiControllerBuilder<T> ForAll<T>(Assembly assembly, string servicePrefix) { return new BatchApiControllerBuilder<T>(assembly, servicePrefix); }
這個方法爲BatchApiControllerBuilder提供了服務接口與服務接口與須要分析的程序集,以及服務地址前綴。 spa
BatchApiControllerBuilder從程序集中獲取實現服務接口的非抽象類。BatchApiControllerBuilder再經過DynamicApiControllerBuilder將這些類與服務名信息傳遞給IApiControllerBuilder。設計
public void Build() { var types = from type in _assembly.GetTypes() where type.IsPublic && type.IsInterface && typeof(T).IsAssignableFrom(type) && IocManager.Instance.IsRegistered(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(DynamicApiControllerBuilder) .GetMethod("For", BindingFlags.Public | BindingFlags.Static) .MakeGenericMethod(type) .Invoke(null, new object[] { serviceName }); if (_filters != null) { builder.GetType() .GetMethod("WithFilters", BindingFlags.Public | BindingFlags.Instance) .Invoke(builder, new object[] { _filters }); } builder.GetType() .GetMethod("Build", BindingFlags.Public | BindingFlags.Instance) .Invoke(builder, new object[0]); } }
IApiControllerBuilder將經過服務類生成DynamicApiControllerInfo,再將IApiControllerBuilder存儲於DynamicApiControllerManager中,同時分析服務類,將公開非靜態方法做爲action,存儲到DynamicApiControllerManager.Actions code
internal class ApiControllerBuilder<T> : IApiControllerBuilder<T> { /// <summary> /// Name of the controller. /// </summary> private readonly string _serviceName; /// <summary> /// List of all action builders for this controller. /// </summary> private readonly IDictionary<string, ApiControllerActionBuilder<T>> _actionBuilders; /// <summary> /// Action Filters to apply to the whole Dynamic Controller. /// </summary> private IFilter[] _filters; /// <summary> /// Creates a new instance of ApiControllerInfoBuilder. /// </summary> /// <param name="serviceName">Name of the controller</param> public ApiControllerBuilder(string serviceName) { if (string.IsNullOrWhiteSpace(serviceName)) { throw new ArgumentException("serviceName null or empty!", "serviceName"); } if (!DynamicApiServiceNameHelper.IsValidServiceName(serviceName)) { throw new ArgumentException("serviceName is not properly formatted! It must contain a single-depth namespace at least! For example: 'myapplication/myservice'.", "serviceName"); } _serviceName = serviceName; _actionBuilders = new Dictionary<string, ApiControllerActionBuilder<T>>(); foreach (var methodInfo in DynamicApiControllerActionHelper.GetMethodsOfType(typeof(T))) { _actionBuilders[methodInfo.Name] = new ApiControllerActionBuilder<T>(this, methodInfo); } } /// <summary> /// The adds Action filters for the whole Dynamic Controller /// </summary> /// <param name="filters"> The filters. </param> /// <returns>The current Controller Builder </returns> public IApiControllerBuilder<T> WithFilters(params IFilter[] filters) { _filters = filters; return this; } /// <summary> /// Used to specify a method definition. /// </summary> /// <param name="methodName">Name of the method in proxied type</param> /// <returns>Action builder</returns> public IApiControllerActionBuilder<T> ForMethod(string methodName) { if (!_actionBuilders.ContainsKey(methodName)) { throw new AbpException("There is no method with name " + methodName + " in type " + typeof(T).Name); } return _actionBuilders[methodName]; } /// <summary> /// Builds the controller. /// This method must be called at last of the build operation. /// </summary> public void Build() { var controllerInfo = new DynamicApiControllerInfo(_serviceName, typeof(DynamicApiController<T>), _filters); foreach (var actionBuilder in _actionBuilders.Values) { if (actionBuilder.DontCreate) { continue; } controllerInfo.Actions[actionBuilder.ActionName] = actionBuilder.BuildActionInfo(); } IocManager.Instance.IocContainer.Register( Component.For<AbpDynamicApiControllerInterceptor<T>>().LifestyleTransient(), Component.For<DynamicApiController<T>>().Proxy.AdditionalInterfaces(new[] { typeof(T) }).Interceptors<AbpDynamicApiControllerInterceptor<T>>().LifestyleTransient() ); DynamicApiControllerManager.Register(controllerInfo); LogHelper.Logger.DebugFormat("Dynamic web api controller is created for type '{0}' with service name '{1}'.", typeof(T).FullName, controllerInfo.ServiceName); } }