123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454 |
- using System;
- using System.Linq;
- using System.Reflection;
- using System.Text.RegularExpressions;
- using System.Threading.Tasks;
- using Microsoft.AspNetCore.Mvc;
- using Microsoft.AspNetCore.Mvc.ActionConstraints;
- using Microsoft.AspNetCore.Mvc.ApplicationModels;
- using Microsoft.AspNetCore.Mvc.ModelBinding;
- using ZhonTai.DynamicApi.Attributes;
- using ZhonTai.DynamicApi.Enums;
- using ZhonTai.DynamicApi.Helpers;
- namespace ZhonTai.DynamicApi;
- public class DynamicApiConvention : IApplicationModelConvention
- {
- private readonly ISelectController _selectController;
- private readonly IActionRouteFactory _actionRouteFactory;
- public DynamicApiConvention(ISelectController selectController, IActionRouteFactory actionRouteFactory)
- {
- _selectController = selectController;
- _actionRouteFactory = actionRouteFactory;
- }
- public string GetSeparateWords(string value, NamingConventionEnum namingConvention = NamingConventionEnum.KebabCase)
- {
- if (string.IsNullOrWhiteSpace(value))
- return value;
- var separator = "-";
- if (namingConvention == NamingConventionEnum.SnakeCase)
- {
- separator = "_";
- }
- else if (namingConvention == NamingConventionEnum.ExtensionCase)
- {
- separator = ".";
- }
- return Regex.Replace(
- value,
- "(?<!^)([A-Z][a-z]|(?<=[a-z])[A-Z])",
- $"{separator}$1",
- RegexOptions.Compiled)
- .Trim()
- .ToLower();
- }
- public string GetFormatName(string value, NamingConventionEnum namingConvention = NamingConventionEnum.KebabCase)
- {
- if (namingConvention == NamingConventionEnum.KebabCase ||
- namingConvention == NamingConventionEnum.SnakeCase ||
- namingConvention == NamingConventionEnum.ExtensionCase)
- {
- return GetSeparateWords(value, namingConvention);
- }
- else if (namingConvention == NamingConventionEnum.PascalCase)
- {
- return value.FirstCharToUpper();
- }
- else if (namingConvention == NamingConventionEnum.CamelCase)
- {
- return value.FirstCharToLower();
- }
- return value;
- }
- 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.NamingConvention == NamingConventionEnum.Custom)
- {
- controller.ControllerName = GetRestFulControllerName(controller.ControllerName);
- }
- else
- {
- controller.ControllerName = GetFormatName(controller.ControllerName, AppConsts.NamingConvention);
- }
- ConfigureDynamicApi(controller, DynamicApiAttr);
- }
- else
- {
- if (typeof(IDynamicApi).GetTypeInfo().IsAssignableFrom(type))
- {
- controller.ControllerName = controller.ControllerName.RemovePostFix(AppConsts.ControllerPostfixes.ToArray());
- if (AppConsts.NamingConvention == NamingConventionEnum.Custom)
- {
- controller.ControllerName = GetRestFulControllerName(controller.ControllerName);
- }
- else
- {
- controller.ControllerName = GetFormatName(controller.ControllerName, AppConsts.NamingConvention);
- }
- ConfigureArea(controller, DynamicApiAttr);
- ConfigureDynamicApi(controller, DynamicApiAttr);
- }
- else
- {
- if (DynamicApiAttr != null)
- {
- ConfigureArea(controller, DynamicApiAttr);
- ConfigureDynamicApi(controller, DynamicApiAttr);
- }
- }
- }
- }
- }
- 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);
- if (AppConsts.FormatResult)
- {
- ConfigureFormatResult(controller);
- }
- }
- private void ConfigureFormatResult(ControllerModel controller)
- {
- foreach (var action in controller.Actions)
- {
- if (!CheckNoMapMethod(action))
- {
- var returnType = action.ActionMethod.GetReturnType();
- if (returnType == typeof(void)) return;
- action.Filters.Add(new FormatResultAttribute(returnType));
- }
- }
- }
- 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.NamingConvention == NamingConventionEnum.Custom)
- {
- action.ActionName = GetRestFulActionName(action.ActionName);
- }
- else
- {
- action.ActionName = GetFormatName(action.ActionName, AppConsts.NamingConvention);
- }
- 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.NamingConvention == NamingConventionEnum.Custom)
- {
- action.ActionName = GetRestFulActionName(action.ActionName);
- }
- else
- {
- action.ActionName = GetFormatName(action.ActionName, AppConsts.NamingConvention);
- }
- 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));
- }
- }
|