ABP动态生成WebAPI

Posted 波峰浪尖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ABP动态生成WebAPI相关的知识,希望对你有一定的参考价值。

ABP框架可以动态生成WebApi,开发人员无需创建APIController,直接继承IApplicationService接口,即可对外发布webApi。

创建动态Web 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 访问。

实现原理:

模块初始化阶段,注册动态API实现模块,在批量/单个注册方法中,执行“BatchApiControllerBuilder”的“Build”方法,遍历Application层程序集,查找所有已注册接口类型。然后根据类型信息获取服务名称,生成单个“ApiControllerBuilder”实例,依次执行“ApiControllerBuilder”中的方法。其中生成“action”是在“Builder”中实现的。

在“ApiControllerBuilder”的“Builder”方法中”,通过“Build”方法构建apiinfo信息并将“action”添加到controller中,最后在apicontroller管理类中注册controller信息。

以下对类和接口逐一分析

AbpApiController:集成了ApiController,框架中自定义的apicontroller都继承自该类;

IDynamicApiController:空接口,生成DynamicApiController标识;

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         }

 


以上是关于ABP动态生成WebAPI的主要内容,如果未能解决你的问题,请参考以下文章

给WebApi添加SwaggerUI,生成可交互接口文档

ABP官方文档翻译 5.2 动态We API层

ABP开发框架前后端开发系列---(11)菜单的动态管理

ABP 用swagger UI测试API报401无权限访问问题

ABP WebApi 加载错误

ABP中的模块初始化过程