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
[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實現對象
public static IBatchApiControllerBuilder<T> ForAll<T>(Assembly assembly, string servicePrefix)
{
return new BatchApiControllerBuilder<T>(assembly, servicePrefix);
}
這個方法為BatchApiControllerBuilder提供了服務接口與服務接口與需要分析的程序集,以及服務地址前綴。
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
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);
}
}
