using NCC.Dependency;
using NCC.Extensions;
using NCC.UnifyResult;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Routing;
using System;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
namespace NCC.DynamicApiController
{
///
/// 动态接口控制器应用模型转换器
///
internal sealed class DynamicApiControllerApplicationModelConvention : IApplicationModelConvention
{
///
/// 动态接口控制器配置实例
///
private readonly DynamicApiControllerSettingsOptions _dynamicApiControllerSettings;
///
/// 带版本的名称正则表达式
///
private readonly Regex _nameVersionRegex;
///
/// 构造函数
///
public DynamicApiControllerApplicationModelConvention()
{
_dynamicApiControllerSettings = App.GetConfig("DynamicApiControllerSettings", true);
LoadVerbToHttpMethodsConfigure();
_nameVersionRegex = new Regex(@"V(?[0-9_]+$)");
}
///
/// 配置应用模型信息
///
/// 引用模型
public void Apply(ApplicationModel application)
{
var controllers = application.Controllers.Where(u => Penetrates.IsApiController(u.ControllerType));
foreach (var controller in controllers)
{
var controllerType = controller.ControllerType;
// 判断是否处理 Mvc控制器
if (typeof(ControllerBase).IsAssignableFrom(controller.ControllerType))
{
if (!_dynamicApiControllerSettings.SupportedMvcController.Value || controller.ApiExplorer?.IsVisible == false) continue;
}
var controllerApiDescriptionSettings = controllerType.IsDefined(typeof(ApiDescriptionSettingsAttribute), true) ? controllerType.GetCustomAttribute(true) : default;
ConfigureController(controller, controllerApiDescriptionSettings);
}
}
///
/// 配置控制器
///
/// 控制器模型
/// 接口描述配置
private void ConfigureController(ControllerModel controller, ApiDescriptionSettingsAttribute controllerApiDescriptionSettings)
{
// 配置区域
ConfigureControllerArea(controller, controllerApiDescriptionSettings);
// 配置控制器名称
ConfigureControllerName(controller, controllerApiDescriptionSettings);
// 存储排序给 Swagger 使用
Penetrates.ControllerOrderCollection.TryAdd(controller.ControllerName, controllerApiDescriptionSettings?.Order ?? 0);
var actions = controller.Actions;
// 查找所有重复的方法签名
var repeats = actions.GroupBy(u => new { u.ActionMethod.ReflectedType.Name, Signature = u.ActionMethod.ToString() })
.Where(u => u.Count() > 1)
.SelectMany(u => u.Where(u => u.ActionMethod.ReflectedType.Name != u.ActionMethod.DeclaringType.Name));
// 2021年04月01日 https://docs.microsoft.com/en-US/aspnet/core/web-api/?view=aspnetcore-5.0#binding-source-parameter-inference
// 判断是否贴有 [ApiController] 特性
var hasApiControllerAttribute = controller.Attributes.Any(u => u.GetType() == typeof(ApiControllerAttribute));
foreach (var action in actions)
{
// 跳过相同方法签名
if (repeats.Contains(action))
{
action.ApiExplorer.IsVisible = false;
continue;
};
var actionMethod = action.ActionMethod;
var actionApiDescriptionSettings = actionMethod.IsDefined(typeof(ApiDescriptionSettingsAttribute), true) ? actionMethod.GetCustomAttribute(true) : default;
ConfigureAction(action, actionApiDescriptionSettings, controllerApiDescriptionSettings, hasApiControllerAttribute);
}
}
///
/// 配置控制器区域
///
///
///
private void ConfigureControllerArea(ControllerModel controller, ApiDescriptionSettingsAttribute controllerApiDescriptionSettings)
{
// 如果配置了区域,则跳过
if (controller.RouteValues.ContainsKey("area")) return;
// 如果没有配置区域,则跳过
var area = controllerApiDescriptionSettings?.Area ?? _dynamicApiControllerSettings.DefaultArea;
if (string.IsNullOrWhiteSpace(area)) return;
controller.RouteValues["area"] = area;
}
///
/// 配置控制器名称
///
/// 控制器模型
/// 接口描述配置
private void ConfigureControllerName(ControllerModel controller, ApiDescriptionSettingsAttribute controllerApiDescriptionSettings)
{
var (Name, _, _) = ConfigureControllerAndActionName(controllerApiDescriptionSettings, controller.ControllerType.Name, _dynamicApiControllerSettings.AbandonControllerAffixes, _ => _);
controller.ControllerName = Name;
}
///
/// 配置动作方法
///
/// 控制器模型
/// 接口描述配置
/// 控制器接口描述配置
/// 是否贴有 ApiController 特性
private void ConfigureAction(ActionModel action, ApiDescriptionSettingsAttribute apiDescriptionSettings, ApiDescriptionSettingsAttribute controllerApiDescriptionSettings, bool hasApiControllerAttribute)
{
// 配置动作方法接口可见性
ConfigureActionApiExplorer(action);
// 配置动作方法名称
var (isLowercaseRoute, isKeepName) = ConfigureActionName(action, apiDescriptionSettings, controllerApiDescriptionSettings);
// 配置动作方法请求谓词特性
ConfigureActionHttpMethodAttribute(action);
// 配置引用类型参数
ConfigureClassTypeParameter(action);
// 配置动作方法路由特性
ConfigureActionRouteAttribute(action, apiDescriptionSettings, controllerApiDescriptionSettings, isLowercaseRoute, isKeepName, hasApiControllerAttribute);
// 配置动作方法规范化特性
if (UnifyContext.EnabledUnifyHandler) ConfigureActionUnifyResultAttribute(action);
}
///
/// 配置动作方法接口可见性
///
/// 动作方法模型
private static void ConfigureActionApiExplorer(ActionModel action)
{
if (!action.ApiExplorer.IsVisible.HasValue) action.ApiExplorer.IsVisible = true;
}
///
/// 配置动作方法名称
///
/// 动作方法模型
/// 接口描述配置
///
///
private (bool IsLowercaseRoute, bool IsKeepName) ConfigureActionName(ActionModel action, ApiDescriptionSettingsAttribute apiDescriptionSettings, ApiDescriptionSettingsAttribute controllerApiDescriptionSettings)
{
var (Name, IsLowercaseRoute, IsKeepName) = ConfigureControllerAndActionName(apiDescriptionSettings, action.ActionMethod.Name, _dynamicApiControllerSettings.AbandonActionAffixes, (tempName) =>
{
// 处理动作方法名称谓词
if (!CheckIsKeepVerb(apiDescriptionSettings, controllerApiDescriptionSettings))
{
var words = tempName.SplitCamelCase();
var verbKey = words.First().ToLower();
// 处理类似 getlist,getall 多个单词
if (words.Length > 1 && Penetrates.VerbToHttpMethods.ContainsKey((words[0] + words[1]).ToLower()))
{
tempName = tempName[(words[0] + words[1]).Length..];
}
else if (Penetrates.VerbToHttpMethods.ContainsKey(verbKey)) tempName = tempName[verbKey.Length..];
}
return tempName;
}, controllerApiDescriptionSettings);
action.ActionName = Name;
return (IsLowercaseRoute, IsKeepName);
}
///
/// 配置动作方法请求谓词特性
///
/// 动作方法模型
private void ConfigureActionHttpMethodAttribute(ActionModel action)
{
var selectorModel = action.Selectors[0];
// 跳过已配置请求谓词特性的配置
if (selectorModel.ActionConstraints.Count > 0) return;
// 解析请求谓词
var words = action.ActionMethod.Name.SplitCamelCase();
var verbKey = words.First().ToLower();
// 处理类似 getlist,getall 多个单词
if (words.Length > 1 && Penetrates.VerbToHttpMethods.ContainsKey((words[0] + words[1]).ToLower()))
{
verbKey = (words[0] + words[1]).ToLower();
}
var verb = Penetrates.VerbToHttpMethods.ContainsKey(verbKey)
? Penetrates.VerbToHttpMethods[verbKey] ?? _dynamicApiControllerSettings.DefaultHttpMethod.ToUpper()
: _dynamicApiControllerSettings.DefaultHttpMethod.ToUpper();
// 添加请求约束
selectorModel.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { verb }));
// 添加请求谓词特性
HttpMethodAttribute httpMethodAttribute = verb switch
{
"GET" => new HttpGetAttribute(),
"POST" => new HttpPostAttribute(),
"PUT" => new HttpPutAttribute(),
"DELETE" => new HttpDeleteAttribute(),
"PATCH" => new HttpPatchAttribute(),
"HEAD" => new HttpHeadAttribute(),
_ => throw new NotSupportedException($"{verb}")
};
selectorModel.EndpointMetadata.Add(httpMethodAttribute);
}
///
/// 处理类类型参数(添加[FromBody] 特性)
///
///
private void ConfigureClassTypeParameter(ActionModel action)
{
// 没有参数无需处理
if (action.Parameters.Count == 0) return;
// 如果动作方法请求谓词只有GET和HEAD,则将类转查询参数
if (_dynamicApiControllerSettings.ModelToQuery.Value)
{
var httpMethods = action.Selectors
.SelectMany(u => u.ActionConstraints.Where(u => u is HttpMethodActionConstraint)
.SelectMany(u => (u as HttpMethodActionConstraint).HttpMethods));
if (httpMethods.All(u => u.Equals("GET") || u.Equals("HEAD"))) return;
}
var parameters = action.Parameters;
foreach (var parameterModel in parameters)
{
// 如果参数已有绑定特性,则跳过
if (parameterModel.BindingInfo != null) continue;
var parameterType = parameterModel.ParameterType;
// 如果是基元类型,则跳过
if (parameterType.IsRichPrimitive()) continue;
// 如果是文件类型,则跳过
if (typeof(IFormFile).IsAssignableFrom(parameterType) || typeof(IFormFileCollection).IsAssignableFrom(parameterType)) continue;
parameterModel.BindingInfo = BindingInfo.GetBindingInfo(new[] { new FromBodyAttribute() });
}
}
///
/// 配置动作方法路由特性
///
/// 动作方法模型
/// 接口描述配置
/// 控制器接口描述配置
///
///
///
private void ConfigureActionRouteAttribute(ActionModel action, ApiDescriptionSettingsAttribute apiDescriptionSettings, ApiDescriptionSettingsAttribute controllerApiDescriptionSettings, bool isLowercaseRoute, bool isKeepName, bool hasApiControllerAttribute)
{
var selectorModel = action.Selectors[0];
// 跳过已配置路由特性的配置
if (selectorModel.AttributeRouteModel != null) return;
// 读取模块
var module = apiDescriptionSettings?.Module;
string template;
string controllerRouteTemplate = null;
// 如果动作方法名称为空、参数值为空,且无需保留谓词,则只生成控制器路由模板
if (action.ActionName.Length == 0 && !isKeepName && action.Parameters.Count == 0)
{
template = GenerateControllerRouteTemplate(action.Controller, controllerApiDescriptionSettings);
}
else
{
// 生成参数路由模板
var parameterRouteTemplate = GenerateParameterRouteTemplates(action, isLowercaseRoute, hasApiControllerAttribute);
// 生成控制器模板
controllerRouteTemplate = GenerateControllerRouteTemplate(action.Controller, controllerApiDescriptionSettings, parameterRouteTemplate);
// 拼接动作方法路由模板
var ActionStartTemplate = parameterRouteTemplate != null ? (parameterRouteTemplate.ActionStartTemplates.Count == 0 ? null : string.Join("/", parameterRouteTemplate.ActionStartTemplates)) : null;
var ActionEndTemplate = parameterRouteTemplate != null ? (parameterRouteTemplate.ActionEndTemplates.Count == 0 ? null : string.Join("/", parameterRouteTemplate.ActionEndTemplates)) : null;
// 判断是否定义了控制器路由,如果定义,则不拼接控制器路由
template = string.IsNullOrWhiteSpace(controllerRouteTemplate)
? $"{(string.IsNullOrWhiteSpace(module) ? "/" : $"{module}/")}{ActionStartTemplate}/{(string.IsNullOrWhiteSpace(action.ActionName) ? null : "[action]")}/{ActionEndTemplate}"
: $"{controllerRouteTemplate}/{(string.IsNullOrWhiteSpace(module) ? null : $"{module}/")}{ActionStartTemplate}/{(string.IsNullOrWhiteSpace(action.ActionName) ? null : "[action]")}/{ActionEndTemplate}";
}
AttributeRouteModel actionAttributeRouteModel = null;
if (!string.IsNullOrWhiteSpace(template))
{
// 处理多个斜杆问题
template = Regex.Replace(isLowercaseRoute ? template.ToLower() : template, @"\/{2,}", "/");
// 生成路由
actionAttributeRouteModel = string.IsNullOrWhiteSpace(template) ? null : new AttributeRouteModel(new RouteAttribute(template));
}
// 拼接路由
selectorModel.AttributeRouteModel = string.IsNullOrWhiteSpace(controllerRouteTemplate)
? (actionAttributeRouteModel == null ? null : AttributeRouteModel.CombineAttributeRouteModel(action.Controller.Selectors[0].AttributeRouteModel, actionAttributeRouteModel))
: actionAttributeRouteModel;
}
///
/// 生成控制器路由模板
///
///
///
/// 参数路由模板
///
private string GenerateControllerRouteTemplate(ControllerModel controller, ApiDescriptionSettingsAttribute apiDescriptionSettings, ParameterRouteTemplate parameterRouteTemplate = default)
{
var selectorModel = controller.Selectors[0];
// 跳过已配置路由特性的配置
if (selectorModel.AttributeRouteModel != null) return default;
// 读取模块
var module = apiDescriptionSettings?.Module ?? _dynamicApiControllerSettings.DefaultModule;
// 路由默认前缀
var routePrefix = _dynamicApiControllerSettings.DefaultRoutePrefix;
// 生成路由模板
// 如果参数路由模板为空或不包含任何控制器参数模板,则返回正常的模板
if (parameterRouteTemplate == null || (parameterRouteTemplate.ControllerStartTemplates.Count == 0 && parameterRouteTemplate.ControllerEndTemplates.Count == 0))
return $"{(string.IsNullOrWhiteSpace(routePrefix) ? null : $"{routePrefix}/")}{(string.IsNullOrWhiteSpace(module) ? null : $"{module}/")}[controller]";
// 拼接控制器路由模板
var controllerStartTemplate = parameterRouteTemplate.ControllerStartTemplates.Count == 0 ? null : string.Join("/", parameterRouteTemplate.ControllerStartTemplates);
var controllerEndTemplate = parameterRouteTemplate.ControllerEndTemplates.Count == 0 ? null : string.Join("/", parameterRouteTemplate.ControllerEndTemplates);
var template = $"{(string.IsNullOrWhiteSpace(routePrefix) ? null : $"{routePrefix}/")}{(string.IsNullOrWhiteSpace(module) ? null : $"{module}/")}{controllerStartTemplate}/[controller]/{controllerEndTemplate}";
return template;
}
///
/// 生成参数路由模板(非引用类型)
///
/// 动作方法模型
///
///
private ParameterRouteTemplate GenerateParameterRouteTemplates(ActionModel action, bool isLowercaseRoute, bool hasApiControllerAttribute)
{
// 如果没有参数,则跳过
if (action.Parameters.Count == 0) return default;
var parameterRouteTemplate = new ParameterRouteTemplate();
var parameters = action.Parameters;
// 判断是否贴有 [QueryParameters] 特性
var isQueryParametersAction = action.Attributes.Any(u => u is QueryParametersAttribute);
// 遍历所有参数
foreach (var parameterModel in parameters)
{
var parameterType = parameterModel.ParameterType;
var parameterAttributes = parameterModel.Attributes;
// 处理小写参数路由匹配问题
if (isLowercaseRoute) parameterModel.ParameterName = parameterModel.ParameterName.ToLower();
// 判断是否贴有任何 [FromXXX] 特性了
var hasFormAttribute = parameterAttributes.Any(u => typeof(IBindingSourceMetadata).IsAssignableFrom(u.GetType()));
// 判断方法贴有 [QueryParameters] 特性且当前参数没有任何 [FromXXX] 特性,则添加 [FromQuery] 特性
if (isQueryParametersAction && !hasFormAttribute)
{
parameterModel.BindingInfo = BindingInfo.GetBindingInfo(new[] { new FromQueryAttribute() });
continue;
}
// 如果没有贴 [FromRoute] 特性且不是基元类型,则跳过
// 如果没有贴 [FromRoute] 特性且有任何绑定特性,则跳过
if (!parameterAttributes.Any(u => u is FromRouteAttribute)
&& (!parameterType.IsRichPrimitive() || hasFormAttribute)) continue;
// 处理基元数组数组类型,还有全局配置参数问题
if (_dynamicApiControllerSettings?.UrlParameterization == true || parameterType.IsArray)
{
parameterModel.BindingInfo = BindingInfo.GetBindingInfo(new[] { new FromQueryAttribute() });
continue;
}
// 处理 [ApiController] 特性情况
// https://docs.microsoft.com/en-US/aspnet/core/web-api/?view=aspnetcore-5.0#binding-source-parameter-inference
if (!hasFormAttribute && hasApiControllerAttribute) continue;
// 判断是否可以为null
var canBeNull = parameterType.IsGenericType && parameterType.GetGenericTypeDefinition() == typeof(Nullable<>);
// 判断是否贴有路由约束特性
string constraint = default;
if (parameterAttributes.FirstOrDefault(u => u is RouteConstraintAttribute) is RouteConstraintAttribute routeConstraint && !string.IsNullOrWhiteSpace(routeConstraint.Constraint))
{
constraint = !routeConstraint.Constraint.StartsWith(":")
? $":{routeConstraint.Constraint}" : routeConstraint.Constraint;
}
var template = $"{{{parameterModel.ParameterName}{(canBeNull ? "?" : string.Empty)}{constraint}}}";
// 如果没有贴路由位置特性,则默认添加到动作方法后面
if (parameterAttributes.FirstOrDefault(u => u is ApiSeatAttribute) is not ApiSeatAttribute apiSeat)
{
parameterRouteTemplate.ActionEndTemplates.Add(template);
continue;
}
// 生成路由参数位置
switch (apiSeat.Seat)
{
// 控制器名之前
case ApiSeats.ControllerStart:
parameterRouteTemplate.ControllerStartTemplates.Add(template);
break;
// 控制器名之后
case ApiSeats.ControllerEnd:
parameterRouteTemplate.ControllerEndTemplates.Add(template);
break;
// 动作方法名之前
case ApiSeats.ActionStart:
parameterRouteTemplate.ActionStartTemplates.Add(template);
break;
// 动作方法名之后
case ApiSeats.ActionEnd:
parameterRouteTemplate.ActionEndTemplates.Add(template);
break;
default: break;
}
}
return parameterRouteTemplate;
}
///
/// 配置控制器和动作方法名称
///
///
///
///
///
///
///
private (string Name, bool IsLowercaseRoute, bool IsKeepName) ConfigureControllerAndActionName(ApiDescriptionSettingsAttribute apiDescriptionSettings, string orignalName, string[] affixes, Func configure, ApiDescriptionSettingsAttribute controllerApiDescriptionSettings = default)
{
// 获取版本号
var apiVersion = apiDescriptionSettings?.Version;
var isKeepName = false;
// 解析控制器名称
// 判断是否有自定义名称
var tempName = apiDescriptionSettings?.Name;
if (string.IsNullOrWhiteSpace(tempName))
{
// 处理版本号
var (name, version) = ResolveNameVersion(orignalName);
tempName = name;
apiVersion ??= version;
// 清除指定前后缀
tempName = tempName.ClearStringAffixes(affixes: affixes);
isKeepName = CheckIsKeepName(controllerApiDescriptionSettings == null ? null : apiDescriptionSettings, controllerApiDescriptionSettings ?? apiDescriptionSettings);
// 判断是否保留原有名称
if (!isKeepName)
{
// 自定义配置
tempName = configure.Invoke(tempName);
// 处理骆驼命名
if (CheckIsSplitCamelCase(controllerApiDescriptionSettings == null ? null : apiDescriptionSettings, controllerApiDescriptionSettings ?? apiDescriptionSettings))
{
tempName = string.Join(_dynamicApiControllerSettings.CamelCaseSeparator, tempName.SplitCamelCase());
}
}
}
// 拼接名称和版本号
var newName = $"{tempName}{(string.IsNullOrWhiteSpace(apiVersion) ? null : $"{_dynamicApiControllerSettings.VersionSeparator}{apiVersion}")}";
var isLowercaseRoute = CheckIsLowercaseRoute(controllerApiDescriptionSettings == null ? null : apiDescriptionSettings, controllerApiDescriptionSettings ?? apiDescriptionSettings);
return (isLowercaseRoute ? newName.ToLower() : newName, isLowercaseRoute, isKeepName);
}
///
/// 检查是否设置了 KeepName参数
///
///
///
///
private bool CheckIsKeepName(ApiDescriptionSettingsAttribute apiDescriptionSettings, ApiDescriptionSettingsAttribute controllerApiDescriptionSettings)
{
bool isKeepName;
// 判断 Action 是否配置了 KeepName 属性
if (apiDescriptionSettings?.KeepName != null)
{
var canParse = bool.TryParse(apiDescriptionSettings.KeepName.ToString(), out var value);
isKeepName = canParse && value;
}
// 判断 Controller 是否配置了 KeepName 属性
else if (controllerApiDescriptionSettings?.KeepName != null)
{
var canParse = bool.TryParse(controllerApiDescriptionSettings.KeepName.ToString(), out var value);
isKeepName = canParse && value;
}
// 取全局配置
else isKeepName = _dynamicApiControllerSettings?.KeepName == true;
return isKeepName;
}
///
/// 检查是否设置了 KeepVerb 参数
///
///
///
///
private bool CheckIsKeepVerb(ApiDescriptionSettingsAttribute apiDescriptionSettings, ApiDescriptionSettingsAttribute controllerApiDescriptionSettings)
{
bool isKeepVerb;
// 判断 Action 是否配置了 KeepVerb 属性
if (apiDescriptionSettings?.KeepVerb != null)
{
var canParse = bool.TryParse(apiDescriptionSettings.KeepVerb.ToString(), out var value);
isKeepVerb = canParse && value;
}
// 判断 Controller 是否配置了 KeepVerb 属性
else if (controllerApiDescriptionSettings?.KeepVerb != null)
{
var canParse = bool.TryParse(controllerApiDescriptionSettings.KeepVerb.ToString(), out var value);
isKeepVerb = canParse && value;
}
// 取全局配置
else isKeepVerb = _dynamicApiControllerSettings?.KeepVerb == true;
return isKeepVerb;
}
///
/// 判断切割命名参数是否配置
///
///
///
///
private static bool CheckIsSplitCamelCase(ApiDescriptionSettingsAttribute apiDescriptionSettings, ApiDescriptionSettingsAttribute controllerApiDescriptionSettings)
{
bool isSplitCamelCase;
// 判断 Action 是否配置了 SplitCamelCase 属性
if (apiDescriptionSettings?.SplitCamelCase != null)
{
var canParse = bool.TryParse(apiDescriptionSettings.SplitCamelCase.ToString(), out var value);
isSplitCamelCase = !canParse || value;
}
// 判断 Controller 是否配置了 SplitCamelCase 属性
else if (controllerApiDescriptionSettings?.SplitCamelCase != null)
{
var canParse = bool.TryParse(controllerApiDescriptionSettings.SplitCamelCase.ToString(), out var value);
isSplitCamelCase = !canParse || value;
}
// 取全局配置
else isSplitCamelCase = true;
return isSplitCamelCase;
}
///
/// 检查是否启用小写路由
///
///
///
///
private bool CheckIsLowercaseRoute(ApiDescriptionSettingsAttribute apiDescriptionSettings, ApiDescriptionSettingsAttribute controllerApiDescriptionSettings)
{
bool isLowercaseRoute;
// 判断 Action 是否配置了 LowercaseRoute 属性
if (apiDescriptionSettings?.LowercaseRoute != null)
{
var canParse = bool.TryParse(apiDescriptionSettings.LowercaseRoute.ToString(), out var value);
isLowercaseRoute = !canParse || value;
}
// 判断 Controller 是否配置了 LowercaseRoute 属性
else if (controllerApiDescriptionSettings?.LowercaseRoute != null)
{
var canParse = bool.TryParse(controllerApiDescriptionSettings.LowercaseRoute.ToString(), out var value);
isLowercaseRoute = !canParse || value;
}
// 取全局配置
else isLowercaseRoute = (_dynamicApiControllerSettings?.LowercaseRoute) != false;
return isLowercaseRoute;
}
///
/// 配置规范化结果类型
///
///
private static void ConfigureActionUnifyResultAttribute(ActionModel action)
{
// 判断是否手动添加了标注或跳过规范化处理
if (UnifyContext.CheckSucceededNonUnify(action.ActionMethod, out var _, false)) return;
// 获取真实类型
var returnType = action.ActionMethod.GetRealReturnType();
if (returnType == typeof(void)) return;
// 添加规范化结果特性
action.Filters.Add(new UnifyResultAttribute(returnType, StatusCodes.Status200OK));
}
///
/// 解析名称中的版本号
///
/// 名称
/// 名称和版本号
private (string name, string version) ResolveNameVersion(string name)
{
if (!_nameVersionRegex.IsMatch(name)) return (name, default);
var version = _nameVersionRegex.Match(name).Groups["version"].Value.Replace("_", ".");
return (_nameVersionRegex.Replace(name, ""), version);
}
///
/// 获取方法名映射 [HttpMethod] 规则
///
///
private void LoadVerbToHttpMethodsConfigure()
{
var defaultVerbToHttpMethods = Penetrates.VerbToHttpMethods;
// 获取配置的复写映射规则
var verbToHttpMethods = _dynamicApiControllerSettings.VerbToHttpMethods;
if (verbToHttpMethods is not null)
{
// 获取所有参数大于1的配置
var settingsVerbToHttpMethods = verbToHttpMethods
.Where(u => u.Length > 1)
.ToDictionary(u => u[0].ToString().ToLower(), u => u[1]?.ToString());
defaultVerbToHttpMethods.AddOrUpdate(settingsVerbToHttpMethods);
}
}
}
}