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 }