1.应用程序模型
ASP.NET Core MVC根据控制器、操作、操作参数、路由和筛选器的结果,定义模型如下:
ApplicationModel、控制器(ControllerModel)、操作(ActionModel)和参数(ParameterModel)。
上一节中只是告诉系统封哪个是控制器,还要为控制器模型初始化值,比如路由、请求方式(post、get)、方法名,才可以真正实现动态Web API。
2.动态方法实现
2.1 添加AutoApplicationModelConvention
在项目Flavor.AutoWebAPICore,添加AutoApplicationModelConvention。
2.2 AutoApplicationModelConvention代码
AutoApplicationModelConvention
using System.Text;
using Flavor.AutoWebAPICore;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.ApplicationModels;namespace Flavor.AutoWebAPICore
{/// <summary>/// 自动控制器应用模型/// </summary>public class AutoApplicationModelConvention : IApplicationModelConvention{/// <summary>/// 控制器移除的后缀/// </summary>private readonly string _ControllerRemoveSuffix = "AppService";public void Apply(ApplicationModel application){//遍历控制器foreach (var controller in application.Controllers){var type = controller.ControllerType;//设置控制器名称if (controller.ControllerName.EndsWith(_ControllerRemoveSuffix)){controller.ControllerName = controller.ControllerName[..^_ControllerRemoveSuffix.Length];}//控制器继承于IAutoControllerif (typeof(IAutoController).IsAssignableFrom(type)){//设置API对外可见ConfigureApiExplorer(controller);//设置控制器选择器ConfigureSelector(controller);}}}/// <summary>/// 设置API对外可见/// </summary>/// <param name="controller"></param>private static void ConfigureApiExplorer(ControllerModel controller){if (!controller.ApiExplorer.IsVisible.HasValue){controller.ApiExplorer.IsVisible = true;}foreach (var action in controller.Actions){if (!action.ApiExplorer.IsVisible.HasValue){action.ApiExplorer.IsVisible = true;}}}/// <summary>/// 设置控制器选择器/// </summary>/// <param name="controller"></param>private void ConfigureSelector(ControllerModel controller){//移除空的选择器RemoveEmptySelectors(controller.Selectors);if (controller.Selectors.Any(selector => selector.AttributeRouteModel != null)){return;}//遍历控制器的方法foreach (var action in controller.Actions){//设置方法的选择器ConfigureSelector(action);}}/// <summary>/// 移除空的选择器/// </summary>/// <param name="selectors"></param>private static void RemoveEmptySelectors(IList<SelectorModel> selectors){for (var i = selectors.Count - 1; i >= 0; i--){var selector = selectors[i];if (selector.AttributeRouteModel == null &&(selector.ActionConstraints == null || selector.ActionConstraints.Count <= 0) &&(selector.EndpointMetadata == null || selector.EndpointMetadata.Count <= 0)){selectors.Remove(selector);}}}/// <summary>/// 设置方法的选择器/// </summary>/// <param name="action"></param>private void ConfigureSelector(ActionModel action){RemoveEmptySelectors(action.Selectors);if (action.Selectors.Count <= 0){//添加选择器AddServiceSelector(action);}else{//格式化选择器NormalizeSelectorRoutes(action);}}/// <summary>/// 为方法添加选择器:路由、Http请求方式/// </summary>/// <param name="action"></param>private void AddServiceSelector(ActionModel action){var httpMothod = GetHttpMethod(action);var template = new Microsoft.AspNetCore.Mvc.RouteAttribute(GetRouteTemplate(action));var selector = new SelectorModel{AttributeRouteModel = new AttributeRouteModel(template)};selector.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { httpMothod }));action.Selectors.Add(selector);}/// <summary>/// 格式化方法选择器:路由、Http请求方式/// </summary>/// <param name="action"></param>private void NormalizeSelectorRoutes(ActionModel action){foreach (var selector in action.Selectors){var httpMothod = GetHttpMethod(action);if (selector.AttributeRouteModel == null){var template = new Microsoft.AspNetCore.Mvc.RouteAttribute(GetRouteTemplate(action));selector.AttributeRouteModel = new AttributeRouteModel(template);}if (selector.ActionConstraints.OfType<HttpMethodActionConstraint>().FirstOrDefault()?.HttpMethods?.FirstOrDefault() == null){selector.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { httpMothod }));}}}/// <summary>/// 获取路由/// </summary>/// <param name="action"></param>/// <returns></returns>private string GetRouteTemplate(ActionModel action){//路由var routeTemplate = new StringBuilder();// 控制器var controllerName = action.Controller.ControllerName;if (controllerName.EndsWith(_ControllerRemoveSuffix)){controllerName = controllerName[0..^_ControllerRemoveSuffix.Length];}routeTemplate.Append($"/{controllerName}");// 方法var actionName = action.ActionName;if (!string.IsNullOrEmpty(actionName)){routeTemplate.Append($"/{actionName}");}return routeTemplate.ToString();}/// <summary>/// 获取请求方式/// </summary>/// <param name="action"></param>/// <returns></returns>private static string GetHttpMethod(ActionModel action){var actionName = action.ActionName;if (actionName.StartsWith("Get")){return "Get";}else if (actionName.StartsWith("Put")){return "Put";}else if (actionName.StartsWith("Delete")){return "Delete";}else if (actionName.StartsWith("Options")){return "Options";}else if (actionName.StartsWith("Trace")){return "Trace";}else if (actionName.StartsWith("Head")){return "Head";}else if (actionName.StartsWith("Patch")){return "Patch";}return "Post";}}
}
代码说明:
1、AutoApplicationModelConvention继承IApplicationModelConvention,并实现接口:apply绑定控制器、方法模型。
2、其中方法:ConfigureApiExplorer,设置控制器、方法对外可见。通过设置属性:ApiExplorer。在Asp.net core中,API默认对外可见,如果没设置也可以。
3、方法:ConfigureSelector,用于初始化路由、Http请求方法。
2.3 添加动态方法
在Program.cs项目入口,添加动态方法。
Program.cs
//添加动态方法
builder.Services.AddMvc(m =>
{m.Conventions.Add(new AutoApplicationModelConvention());
});
2.4 测试
测试效果如下:
1、控制器名称,路由名称都已去掉后缀:AppService。
2、接口正常请求结果。
3、请求方法为Get。