ABP動態生成WebAPI

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
    }
View Code

 

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         }
相關文章
相關標籤/搜索