/// <summary> /// Initializes the type of the API. /// </summary> /// <param name="doneInterfaceTypes">The done interface types.</param> /// <param name="routes">The routes.</param> /// <param name="interfaceType">Type of the interface.</param> /// <param name="instance">The instance.</param> /// <param name="settings">The settings.</param> /// <param name="parentApiContractAttribute">The parent API class attribute.</param> /// <param name="parentApiModuleAttribute">The parent API module attribute.</param> protected void InitializeApiType(List<string> doneInterfaceTypes, Dictionary<string, RuntimeRoute> routes, Type interfaceType, object instance, RestApiSettings settings = null, ApiContractAttribute parentApiContractAttribute = null, ApiModuleAttribute parentApiModuleAttribute = null) { if (routes != null && interfaceType != null && doneInterfaceTypes != null) { if (doneInterfaceTypes.Contains(interfaceType.FullName)) { return; } var ApiContract = parentApiContractAttribute ?? interfaceType.GetCustomAttribute<ApiContractAttribute>(true); var apiModule = parentApiModuleAttribute ?? interfaceType.GetCustomAttribute<ApiModuleAttribute>(true); var moduleName = apiModule?.ToString(); if (ApiContract != null && !string.IsNullOrWhiteSpace(ApiContract.Version)) { var apiContractName = ApiContract.Name.SafeToString(interfaceType.FullName); if (ApiContract.Version.SafeToLower().Equals(BuildInFeatureVersionKeyword)) { throw new InvalidObjectException("ApiContract.Version", reason: "<buildin> cannot be used as version due to it is used internally."); } foreach (var method in interfaceType.GetMethods()) { var apiOperationAttribute = method.GetCustomAttribute<ApiOperationAttribute>(true); var apiTransportAttribute = method.GetCustomAttribute<ApiTransportAttribute>(); #region Initialize based on ApiOperation if (apiOperationAttribute != null) { var permissions = new Dictionary<string, ApiPermission>(); var additionalHeaderKeys = new HashSet<string>(); var apiPermissionAttributes = method.GetCustomAttributes<ApiPermissionAttribute>(true); if (apiPermissionAttributes != null) { foreach (var one in apiPermissionAttributes) { permissions.Merge(one.PermissionIdentifier, one.Permission); } } var headerKeyAttributes = method.GetCustomAttributes<ApiHeaderAttribute>(true); if (headerKeyAttributes != null) { foreach (var one in headerKeyAttributes) { additionalHeaderKeys.Add(one.HeaderKey); } } var routeKey = GetRouteKey(ApiContract.Version, apiOperationAttribute.ResourceName, apiOperationAttribute.HttpMethod, apiOperationAttribute.Action); RuntimeRoute runtimeRoute = null; if (apiTransportAttribute != null) { runtimeRoute = new RuntimeRoute(apiTransportAttribute); } else { var tokenRequired = method.GetCustomAttribute<TokenRequiredAttribute>(true) ?? interfaceType.GetCustomAttribute<TokenRequiredAttribute>(true); runtimeRoute = new RuntimeRoute(method, interfaceType, instance, !string.IsNullOrWhiteSpace(apiOperationAttribute.Action), tokenRequired != null && tokenRequired.TokenRequired, moduleName, apiContractName, settings, permissions, additionalHeaderKeys.ToList()); } if (routes.ContainsKey(routeKey)) { throw new DataConflictException("Route", objectIdentity: routeKey, data: new { existed = routes[routeKey].SafeToString(), newMethod = method.GetFullName(), newInterface = interfaceType.FullName }); } routes.Add(routeKey, runtimeRoute); } #endregion } foreach (var one in interfaceType.GetInterfaces()) { InitializeApiType(doneInterfaceTypes, routes, one, instance, settings, ApiContract, apiModule); } } doneInterfaceTypes.Add(interfaceType.FullName); } }
/// <summary> /// Initializes the type of the API. /// </summary> /// <param name="doneInterfaceTypes">The done interface types.</param> /// <param name="routes">The routes.</param> /// <param name="interfaceType">Type of the interface.</param> /// <param name="instance">The instance.</param> /// <param name="settings">The settings.</param> /// <param name="parentApiContractAttribute">The parent API class attribute.</param> /// <param name="parentApiModuleAttribute">The parent API module attribute.</param> protected void InitializeApiType(List<string> doneInterfaceTypes, Dictionary<string, RuntimeRoute> routes, Type interfaceType, object instance, RestApiSettings settings = null, ApiContractAttribute parentApiContractAttribute = null, ApiModuleAttribute parentApiModuleAttribute = null) { if (routes != null && interfaceType != null && doneInterfaceTypes != null) { if (doneInterfaceTypes.Contains(interfaceType.FullName)) { return; } var apiContract = parentApiContractAttribute ?? interfaceType.GetCustomAttribute<ApiContractAttribute>(true); var apiModule = parentApiModuleAttribute ?? interfaceType.GetCustomAttribute<ApiModuleAttribute>(true); var moduleName = apiModule?.ToString(); if (apiContract != null && !string.IsNullOrWhiteSpace(apiContract.Version)) { if (apiContract.Version.SafeToLower().Equals(BuiltInFeatureVersionKeyword)) { throw ExceptionFactory.CreateInvalidObjectException("apiContract.Version", reason: "<builtin> cannot be used as version due to it is used internally."); } foreach (var method in interfaceType.GetMethods()) { var apiOperationAttribute = method.GetCustomAttribute<ApiOperationAttribute>(true); #region Initialize based on ApiOperation if (apiOperationAttribute != null) { var permissions = new Dictionary<string, ApiPermission>(); var additionalHeaderKeys = new HashSet<string>(); var apiPermissionAttributes = method.GetCustomAttributes<ApiPermissionAttribute>(true); if (apiPermissionAttributes != null) { foreach (var one in apiPermissionAttributes) { permissions.Merge(one.PermissionIdentifier, one.Permission); } } var headerKeyAttributes = method.GetCustomAttributes<ApiHeaderAttribute>(true); if (headerKeyAttributes != null) { foreach (var one in headerKeyAttributes) { additionalHeaderKeys.Add(one.HeaderKey); } } var routeKey = GetRouteKey(apiContract.Version, apiOperationAttribute.ResourceName, apiOperationAttribute.HttpMethod, apiOperationAttribute.Action); var tokenRequired = method.GetCustomAttribute<TokenRequiredAttribute>(true) ?? interfaceType.GetCustomAttribute<TokenRequiredAttribute>(true); var runtimeRoute = new RuntimeRoute(method, interfaceType, instance, !string.IsNullOrWhiteSpace(apiOperationAttribute.Action), tokenRequired != null && tokenRequired.TokenRequired, moduleName, settings, permissions, additionalHeaderKeys.ToList()); if (routes.ContainsKey(routeKey)) { throw new DataConflictException("Route", objectIdentity: routeKey, data: new { existed = routes[routeKey].SafeToString(), newMethod = method.GetFullName(), newInterface = interfaceType.FullName }); } routes.Add(routeKey, runtimeRoute); } #endregion } foreach (var one in interfaceType.GetInterfaces()) { InitializeApiType(doneInterfaceTypes, routes, one, instance, settings, apiContract, apiModule); } //Special NOTE: // Move this add action in scope of if apiContract is valid. // Reason: in complicated cases, when [A:Interface1] without ApiContract, but [Interface2: Interface] with defining ApiContract, and [B: A, Interface2], then correct contract definition might be missed. doneInterfaceTypes.Add(interfaceType.FullName); } } }