【动态Web API学习(三)】动态方法
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];
}
//控制器继承于IAutoController
if (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。