|  | @@ -0,0 +1,410 @@
 | 
	
		
			
				|  |  | +using System;
 | 
	
		
			
				|  |  | +using System.Collections.Generic;
 | 
	
		
			
				|  |  | +using System.Linq;
 | 
	
		
			
				|  |  | +using System.Reflection;
 | 
	
		
			
				|  |  | +using System.Text.RegularExpressions;
 | 
	
		
			
				|  |  | +using Microsoft.AspNetCore.Authorization;
 | 
	
		
			
				|  |  | +using Microsoft.AspNetCore.Mvc;
 | 
	
		
			
				|  |  | +using Microsoft.AspNetCore.Mvc.ActionConstraints;
 | 
	
		
			
				|  |  | +using Microsoft.AspNetCore.Mvc.ApplicationModels;
 | 
	
		
			
				|  |  | +using Microsoft.AspNetCore.Mvc.ModelBinding;
 | 
	
		
			
				|  |  | +using Microsoft.Extensions.DependencyInjection;
 | 
	
		
			
				|  |  | +using ZhonTai.Tools.DynamicApi.Attributes;
 | 
	
		
			
				|  |  | +using ZhonTai.Tools.DynamicApi.Helpers;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +namespace ZhonTai.Tools.DynamicApi
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    public class DynamicApiConvention : IApplicationModelConvention
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        private readonly ISelectController _selectController;
 | 
	
		
			
				|  |  | +        private readonly IActionRouteFactory _actionRouteFactory;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public DynamicApiConvention(ISelectController selectController, IActionRouteFactory actionRouteFactory)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            _selectController = selectController;
 | 
	
		
			
				|  |  | +            _actionRouteFactory = actionRouteFactory;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public void Apply(ApplicationModel application)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            foreach (var controller in application.Controllers)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                var type = controller.ControllerType.AsType();
 | 
	
		
			
				|  |  | +                var DynamicApiAttr = ReflectionHelper.GetSingleAttributeOrDefaultByFullSearch<DynamicApiAttribute>(type.GetTypeInfo());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                if (!(_selectController is DefaultSelectController) && _selectController.IsController(type))
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    controller.ControllerName = controller.ControllerName.RemovePostFix(AppConsts.ControllerPostfixes.ToArray());
 | 
	
		
			
				|  |  | +                    if (AppConsts.PascalToKebabCase)
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        controller.ControllerName = PascalToKebabCase(controller.ControllerName);
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    else
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        controller.ControllerName = GetRestFulControllerName(controller.ControllerName);
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    
 | 
	
		
			
				|  |  | +                    ConfigureDynamicApi(controller, DynamicApiAttr);
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                else
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    if (typeof(IDynamicApi).GetTypeInfo().IsAssignableFrom(type))
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        controller.ControllerName = controller.ControllerName.RemovePostFix(AppConsts.ControllerPostfixes.ToArray());
 | 
	
		
			
				|  |  | +                        if (AppConsts.PascalToKebabCase)
 | 
	
		
			
				|  |  | +                        {
 | 
	
		
			
				|  |  | +                            controller.ControllerName = PascalToKebabCase(controller.ControllerName);
 | 
	
		
			
				|  |  | +                        }
 | 
	
		
			
				|  |  | +                        else
 | 
	
		
			
				|  |  | +                        {
 | 
	
		
			
				|  |  | +                            controller.ControllerName = GetRestFulControllerName(controller.ControllerName);
 | 
	
		
			
				|  |  | +                        }
 | 
	
		
			
				|  |  | +                        ConfigureArea(controller, DynamicApiAttr);
 | 
	
		
			
				|  |  | +                        ConfigureDynamicApi(controller, DynamicApiAttr);
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    else
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        if (DynamicApiAttr != null)
 | 
	
		
			
				|  |  | +                        {
 | 
	
		
			
				|  |  | +                            ConfigureArea(controller, DynamicApiAttr);
 | 
	
		
			
				|  |  | +                            ConfigureDynamicApi(controller, DynamicApiAttr);
 | 
	
		
			
				|  |  | +                        }
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public string PascalToKebabCase(string value)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            if (string.IsNullOrWhiteSpace(value))
 | 
	
		
			
				|  |  | +                return value;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            return Regex.Replace(
 | 
	
		
			
				|  |  | +                value,
 | 
	
		
			
				|  |  | +                "(?<!^)([A-Z][a-z]|(?<=[a-z])[A-Z])",
 | 
	
		
			
				|  |  | +                "-$1",
 | 
	
		
			
				|  |  | +                RegexOptions.Compiled)
 | 
	
		
			
				|  |  | +                .Trim()
 | 
	
		
			
				|  |  | +                .ToLower();
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private void ConfigureArea(ControllerModel controller, DynamicApiAttribute attr)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            if (!controller.RouteValues.ContainsKey("area"))
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                if (attr == null)
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    throw new ArgumentException(nameof(attr));
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                if (!string.IsNullOrEmpty(attr.Area))
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    controller.RouteValues["area"] = attr.Area;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                else if (!string.IsNullOrEmpty(AppConsts.DefaultAreaName))
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    controller.RouteValues["area"] = AppConsts.DefaultAreaName;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private void ConfigureDynamicApi(ControllerModel controller, DynamicApiAttribute controllerAttr)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            ConfigureApiExplorer(controller);
 | 
	
		
			
				|  |  | +            ConfigureSelector(controller, controllerAttr);
 | 
	
		
			
				|  |  | +            ConfigureParameters(controller);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private void ConfigureParameters(ControllerModel controller)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            foreach (var action in controller.Actions)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                if (!CheckNoMapMethod(action))
 | 
	
		
			
				|  |  | +                    foreach (var para in action.Parameters)
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        if (para.BindingInfo != null)
 | 
	
		
			
				|  |  | +                        {
 | 
	
		
			
				|  |  | +                            continue;
 | 
	
		
			
				|  |  | +                        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                        if (!TypeHelper.IsPrimitiveExtendedIncludingNullable(para.ParameterInfo.ParameterType))
 | 
	
		
			
				|  |  | +                        {
 | 
	
		
			
				|  |  | +                            if (CanUseFormBodyBinding(action, para))
 | 
	
		
			
				|  |  | +                            {
 | 
	
		
			
				|  |  | +                                para.BindingInfo = BindingInfo.GetBindingInfo(new[] { new FromBodyAttribute() });
 | 
	
		
			
				|  |  | +                            }
 | 
	
		
			
				|  |  | +                        }
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private bool CanUseFormBodyBinding(ActionModel action, ParameterModel parameter)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            if (AppConsts.FormBodyBindingIgnoredTypes.Any(t => t.IsAssignableFrom(parameter.ParameterInfo.ParameterType)))
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                return false;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            foreach (var selector in action.Selectors)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                if (selector.ActionConstraints == null)
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    continue;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                foreach (var actionConstraint in selector.ActionConstraints)
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                    var httpMethodActionConstraint = actionConstraint as HttpMethodActionConstraint;
 | 
	
		
			
				|  |  | +                    if (httpMethodActionConstraint == null)
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        continue;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                    if (httpMethodActionConstraint.HttpMethods.All(hm => hm.IsIn("GET", "DELETE", "TRACE", "HEAD")))
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        return false;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            return true;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        #region ConfigureApiExplorer
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private void ConfigureApiExplorer(ControllerModel controller)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            if (controller.ApiExplorer.GroupName.IsNullOrEmpty())
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                controller.ApiExplorer.GroupName = controller.ControllerName;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if (controller.ApiExplorer.IsVisible == null)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                controller.ApiExplorer.IsVisible = true;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            foreach (var action in controller.Actions)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                if (!CheckNoMapMethod(action))
 | 
	
		
			
				|  |  | +                    ConfigureApiExplorer(action);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private void ConfigureApiExplorer(ActionModel action)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            if (action.ApiExplorer.IsVisible == null)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                action.ApiExplorer.IsVisible = true;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        #endregion
 | 
	
		
			
				|  |  | +        /// <summary>
 | 
	
		
			
				|  |  | +        /// //不映射指定的方法
 | 
	
		
			
				|  |  | +        /// </summary>
 | 
	
		
			
				|  |  | +        /// <param name="action"></param>
 | 
	
		
			
				|  |  | +        /// <returns></returns>
 | 
	
		
			
				|  |  | +        private bool CheckNoMapMethod(ActionModel action)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            bool isExist = false;
 | 
	
		
			
				|  |  | +            var noMapMethod = ReflectionHelper.GetSingleAttributeOrDefault<NonDynamicMethodAttribute>(action.ActionMethod);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if (noMapMethod != null)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                action.ApiExplorer.IsVisible = false;//对应的Api不映射
 | 
	
		
			
				|  |  | +                isExist = true;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            return isExist;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        private void ConfigureSelector(ControllerModel controller, DynamicApiAttribute controllerAttr)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if (controller.Selectors.Any(selector => selector.AttributeRouteModel != null))
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                return;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            var areaName = string.Empty;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if (controllerAttr != null)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                areaName = controllerAttr.Area;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            foreach (var action in controller.Actions)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                if (!CheckNoMapMethod(action))
 | 
	
		
			
				|  |  | +                    ConfigureSelector(areaName, controller.ControllerName, action);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private void ConfigureSelector(string areaName, string controllerName, ActionModel action)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            var nonAttr = ReflectionHelper.GetSingleAttributeOrDefault<NonDynamicApiAttribute>(action.ActionMethod);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if (nonAttr != null)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                return;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if (action.Selectors.IsNullOrEmpty() || action.Selectors.Any(a => a.ActionConstraints.IsNullOrEmpty()))
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                if (!CheckNoMapMethod(action))
 | 
	
		
			
				|  |  | +                    AddAppServiceSelector(areaName, controllerName, action);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            else
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                NormalizeSelectorRoutes(areaName, controllerName, action);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private void AddAppServiceSelector(string areaName, string controllerName, ActionModel action)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            var verb = GetHttpVerb(action);
 | 
	
		
			
				|  |  | +            if (AppConsts.PascalToKebabCase)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                action.ActionName = PascalToKebabCase(action.ActionName);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            else
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                action.ActionName = GetRestFulActionName(action.ActionName);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            var appServiceSelectorModel = action.Selectors[0];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if (appServiceSelectorModel.AttributeRouteModel == null)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                appServiceSelectorModel.AttributeRouteModel = CreateActionRouteModel(areaName, controllerName, action);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if (!appServiceSelectorModel.ActionConstraints.Any())
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                appServiceSelectorModel.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { verb }));
 | 
	
		
			
				|  |  | +                switch (verb)
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    case "GET":
 | 
	
		
			
				|  |  | +                        appServiceSelectorModel.EndpointMetadata.Add(new HttpGetAttribute());
 | 
	
		
			
				|  |  | +                        break;
 | 
	
		
			
				|  |  | +                    case "POST":
 | 
	
		
			
				|  |  | +                        appServiceSelectorModel.EndpointMetadata.Add(new HttpPostAttribute());
 | 
	
		
			
				|  |  | +                        break;
 | 
	
		
			
				|  |  | +                    case "PUT":
 | 
	
		
			
				|  |  | +                        appServiceSelectorModel.EndpointMetadata.Add(new HttpPutAttribute());
 | 
	
		
			
				|  |  | +                        break;
 | 
	
		
			
				|  |  | +                    case "DELETE":
 | 
	
		
			
				|  |  | +                        appServiceSelectorModel.EndpointMetadata.Add(new HttpDeleteAttribute());
 | 
	
		
			
				|  |  | +                        break;
 | 
	
		
			
				|  |  | +                    default:
 | 
	
		
			
				|  |  | +                        throw new Exception($"Unsupported http verb: {verb}.");
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        /// <summary>
 | 
	
		
			
				|  |  | +        /// Processing action name
 | 
	
		
			
				|  |  | +        /// </summary>
 | 
	
		
			
				|  |  | +        /// <param name="actionName"></param>
 | 
	
		
			
				|  |  | +        /// <returns></returns>
 | 
	
		
			
				|  |  | +        private static string GetRestFulActionName(string actionName)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            // custom process action name
 | 
	
		
			
				|  |  | +            var appConstsActionName = AppConsts.GetRestFulActionName?.Invoke(actionName);
 | 
	
		
			
				|  |  | +            if (appConstsActionName != null)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                return appConstsActionName;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // default process action name.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // Remove Postfix
 | 
	
		
			
				|  |  | +            actionName = actionName.RemovePostFix(AppConsts.ActionPostfixes.ToArray());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // Remove Prefix
 | 
	
		
			
				|  |  | +            var verbKey = actionName.GetPascalOrCamelCaseFirstWord().ToLower();
 | 
	
		
			
				|  |  | +            if (AppConsts.HttpVerbs.ContainsKey(verbKey))
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                if (actionName.Length == verbKey.Length)
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    return "";
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                else
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    return actionName.Substring(verbKey.Length);
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            else
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                return actionName;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private static string GetRestFulControllerName(string controllerName)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            // custom process action name
 | 
	
		
			
				|  |  | +            var appConstsControllerName = AppConsts.GetRestFulControllerName?.Invoke(controllerName);
 | 
	
		
			
				|  |  | +            if (appConstsControllerName != null)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                return appConstsControllerName;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            else
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                return controllerName;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private void NormalizeSelectorRoutes(string areaName, string controllerName, ActionModel action)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            if (AppConsts.PascalToKebabCase)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                action.ActionName = PascalToKebabCase(action.ActionName);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            else
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                action.ActionName = GetRestFulActionName(action.ActionName);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            
 | 
	
		
			
				|  |  | +            foreach (var selector in action.Selectors)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                selector.AttributeRouteModel = selector.AttributeRouteModel == null ?
 | 
	
		
			
				|  |  | +                     CreateActionRouteModel(areaName, controllerName, action) :
 | 
	
		
			
				|  |  | +                     AttributeRouteModel.CombineAttributeRouteModel(CreateActionRouteModel(areaName, controllerName, action), selector.AttributeRouteModel);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private static string GetHttpVerb(ActionModel action)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            var getValueSuccess = AppConsts.AssemblyDynamicApiOptions
 | 
	
		
			
				|  |  | +                .TryGetValue(action.Controller.ControllerType.Assembly, out AssemblyDynamicApiOptions assemblyDynamicApiOptions);
 | 
	
		
			
				|  |  | +            if (getValueSuccess && !string.IsNullOrWhiteSpace(assemblyDynamicApiOptions?.HttpVerb))
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                return assemblyDynamicApiOptions.HttpVerb;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            var verbKey = action.ActionName.GetPascalOrCamelCaseFirstWord().ToLower();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            var verb = AppConsts.HttpVerbs.ContainsKey(verbKey) ? AppConsts.HttpVerbs[verbKey] : AppConsts.DefaultHttpVerb;
 | 
	
		
			
				|  |  | +            return verb;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private AttributeRouteModel CreateActionRouteModel(string areaName, string controllerName, ActionModel action)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            var route =  _actionRouteFactory.CreateActionRouteModel(areaName, controllerName, action);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            return new AttributeRouteModel(new RouteAttribute(route));
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 |