ABP之動態WebAPI(一)

ABP的動態WebApi實現了直接對服務層的調用(其實病沒有跨過ApiController,只是將ApiController公共化,對於這一點的處理相似於MVC,對服務端的 調用沒有跨過HttpHandler同樣),這樣不只減小了ApiController的開發,也更能體現驅動領域設計的層結構。web

 

對WebApi服務的替換與路由配置

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}"
                );
        }
View Code

 

 

 

 

對服務的分析與緩存

再對服務信息的存儲上,做者提供了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);
        }
    }
View Code

 

 

 

在執行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);
        }
    }
相關文章
相關標籤/搜索