ABP之动态WebAPI(一)
ABP的动态WebApi实现了直接对服务层的调用(其实病没有跨过ApiController,只是将ApiController公共化,对于这一点的处理类似于MVC,对服务端的 调用没有跨过HttpHandler一样),这样不仅减少了ApiController的开发,也更能体现驱动领域设计的层结构。
对WebApi服务的替换与路由配置
AbpWebApiModule是Abp.Web.Api的模块类,该类中定义InitializeAspNetServices,InitializeRoutes两个方法,并且在模块的Initialize方法中执行,这两个方法分别是对WebApi的服务的替换与路由的配置,。这两处对WebApi的变更才使得直接调用服务层成为可能。

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的时候,系统已经在初始化的时候对服务层进行了分析与缓存。
在作者给的Demo SimpleTaskSystem下有一模块类SimpleTaskSystemWebApiModule
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | [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按照注册的服务接口对提供的程序集进行分析。
DynamicApiControllerBuilder提供的ForAll只是返回的一个IBatchApiControllerBuilder实现对象
1 2 3 4 | public static IBatchApiControllerBuilder<T> ForAll<T>(Assembly assembly, string servicePrefix) { return new BatchApiControllerBuilder<T>(assembly, servicePrefix); } |
这个方法为BatchApiControllerBuilder提供了服务接口与服务接口与需要分析的程序集,以及服务地址前缀。
BatchApiControllerBuilder从程序集中获取实现服务接口的非抽象类。BatchApiControllerBuilder再通过DynamicApiControllerBuilder将这些类与服务名信息传递给IApiControllerBuilder。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | 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); } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构