【动态Web API学习(三)】动态方法

1.应用程序模型

ASP.NET Core MVC根据控制器、操作、操作参数、路由和筛选器的结果,定义模型如下:
ApplicationModel、控制器(ControllerModel)、操作(ActionModel)和参数(ParameterModel)。

上一节中只是告诉系统封哪个是控制器,还要为控制器模型初始化值,比如路由、请求方式(post、get)、方法名,才可以真正实现动态Web API。

2.动态方法实现

2.1 添加AutoApplicationModelConvention

在项目Flavor.AutoWebAPICore,添加AutoApplicationModelConvention。
image

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());
});

image

2.4 测试

测试效果如下:
1、控制器名称,路由名称都已去掉后缀:AppService。
2、接口正常请求结果。
3、请求方法为Get。
image

posted @ 2024-10-02 14:32  羊徒  阅读(42)  评论(0编辑  收藏  举报