首页 > 其他分享 >.net 动态api

.net 动态api

时间:2023-05-11 22:48:33浏览次数:39  
标签:actionName return var controller api action new net 动态

本文在webapi的基础上进行后续的扩展,也可以实现不依赖项目类型的模式,只需要添加webapi对应的Nuget即可。

  1. 首先创建接口来识别动态API的实现类
public interface IAutoAPIService
    {
    }
  1. ControllerFeatureProvider
    创建一个类继承ControllerFeatureProvider并重写IsController方法,此方法将告诉容器指定的类型是否为控制器。 也可以继承IApplicationFeatureProvider, IApplicationFeatureProvider然后分别实现PopulateFeature、IsController两个方法
public class AutoAPIControllerFeatureProvider :
        ControllerFeatureProvider
        //IApplicationFeatureProvider<ControllerFeature>, IApplicationFeatureProvider
    {
        //public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
        //{
        //    foreach (var part in parts.OfType<IApplicationPartTypeProvider>())
        //    {
        //        foreach (var type in part.Types)
        //        {
        //            if (IsController(type) && !feature.Controllers.Contains(type))
        //            {
        //                feature.Controllers.Add(type);
        //            }
        //        }
        //    }
        //}

        //protected  override bool IsController(TypeInfo typeInfo)
        protected override bool IsController(TypeInfo typeInfo)
        {
            //判断是否继承了指定的接口
            if (typeof(IAutoAPIService).IsAssignableFrom(typeInfo))
            {
                if (!typeInfo.IsInterface &&
                    !typeInfo.IsAbstract &&
                    !typeInfo.IsGenericType &&
                    typeInfo.IsPublic)
                {
                    return true;
                }
            }

            return false;

            //return base.IsController(typeInfo);
        }
  1. IApplicationModelConvention

    public class AutoAPIApplicationModelConvention : IApplicationModelConvention
    {
        public void Apply(ApplicationModel application)
        {
            foreach (var controller in application.Controllers)
            {
                if (typeof(IAutoAPIService).IsAssignableFrom(controller.ControllerType))
                {
                    ConfigureApplicationService(controller);
                }
            }
        }
        private void ConfigureApplicationService(ControllerModel controller)
        {
            ConfigureApiExplorer(controller);//api是否允许被发现
            ConfigureSelector(controller);//路由配置
            ConfigureParameters(controller);//参数配置
        }

        private 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;
                }
            }
        }

        private void ConfigureSelector(ControllerModel controller)
        {
            RemoveEmptySelectors(controller.Selectors);

            if (controller.Selectors.Any(temp => temp.AttributeRouteModel != null))
            {
                return;
            }

            foreach (var action in controller.Actions)
            {
                ConfigureSelector(action);
            }
        }

        private void ConfigureSelector(ActionModel action)
        {
            RemoveEmptySelectors(action.Selectors);

            if (action.Selectors.Count <= 0)
            {
                AddApplicationServiceSelector(action);
            }
            else
            {
                NormalizeSelectorRoutes(action);
            }
        }

        private void ConfigureParameters(ControllerModel controller)
        {
            foreach (var action in controller.Actions)
            {
                foreach (var parameter in action.Parameters)
                {
                    if (parameter.BindingInfo != null)
                    {
                        continue;
                    }

                    if (parameter.ParameterType.IsClass &&
                        parameter.ParameterType != typeof(string) &&
                        parameter.ParameterType != typeof(IFormFile))
                    {
                        var httpMethods = action.Selectors.SelectMany(temp => temp.ActionConstraints).OfType<HttpMethodActionConstraint>().SelectMany(temp => temp.HttpMethods).ToList();
                        if (httpMethods.Contains("GET") ||
                            httpMethods.Contains("DELETE") ||
                            httpMethods.Contains("TRACE") ||
                            httpMethods.Contains("HEAD"))
                        {
                            continue;
                        }

                        parameter.BindingInfo = BindingInfo.GetBindingInfo(new[] { new FromBodyAttribute() });
                    }
                }
            }
        }

        private void NormalizeSelectorRoutes(ActionModel action)
        {
            foreach (var selector in action.Selectors)
            {
                if (selector.AttributeRouteModel == null)
                {
                    selector.AttributeRouteModel = new AttributeRouteModel(new RouteAttribute(CalculateRouteTemplate(action)));
                }

                if (selector.ActionConstraints.OfType<HttpMethodActionConstraint>().FirstOrDefault()?.HttpMethods?.FirstOrDefault() == null)
                {
                    selector.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { GetHttpMethod(action) }));
                }
            }
        }

        private void AddApplicationServiceSelector(ActionModel action)
        {
            var selector = new SelectorModel();
            selector.AttributeRouteModel = new AttributeRouteModel(new RouteAttribute(CalculateRouteTemplate(action)));
            selector.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { GetHttpMethod(action) }));

            action.Selectors.Add(selector);
        }

        private string CalculateRouteTemplate(ActionModel action)
        {
            var routeTemplate = new StringBuilder();
            routeTemplate.Append("api");

            // 控制器名称部分
            var controllerName = action.Controller.ControllerName;
            if (controllerName.EndsWith("ApplicationService"))
            {
                controllerName = controllerName.Substring(0, controllerName.Length - "ApplicationService".Length);
            }
            else if (controllerName.EndsWith("AppService"))
            {
                controllerName = controllerName.Substring(0, controllerName.Length - "AppService".Length);
            }
            controllerName += "s";
            routeTemplate.Append($"/{controllerName}");

            // id 部分
            if (action.Parameters.Any(temp => temp.ParameterName == "id"))
            {
                routeTemplate.Append("/{id}");
            }

            // Action 名称部分
            var actionName = action.ActionName;
            if (actionName.EndsWith("Async"))
            {
                actionName = actionName.Substring(0, actionName.Length - "Async".Length);
            }
            var trimPrefixes = new[]
            {
                "GetAll","GetList","Get",
                "Post","Create","Add","Insert",
                "Put","Update",
                "Delete","Remove",
                "Patch"
            };
            foreach (var trimPrefix in trimPrefixes)
            {
                if (actionName.StartsWith(trimPrefix))
                {
                    actionName = actionName.Substring(trimPrefix.Length);
                    break;
                }
            }
            if (!string.IsNullOrEmpty(actionName))
            {
                routeTemplate.Append($"/{actionName}");
            }

            return routeTemplate.ToString();
        }

        private string GetHttpMethod(ActionModel action)
        {
            var actionName = action.ActionName;
            if (actionName.StartsWith("Get"))
            {
                return "GET";
            }

            if (actionName.StartsWith("Put") || actionName.StartsWith("Update"))
            {
                return "PUT";
            }

            if (actionName.StartsWith("Delete") || actionName.StartsWith("Remove"))
            {
                return "DELETE";
            }

            if (actionName.StartsWith("Patch"))
            {
                return "PATCH";
            }

            return "POST";
        }

        private 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);
                }
            }
        }
    }
  1. 注入
    添加扩展方法来注入
   public static class AutoAPIEx
    {

        public static IMvcBuilder AddDynamicWebApi(this IMvcBuilder builder)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }

            builder.ConfigureApplicationPartManager(applicationPartManager =>
            {
                applicationPartManager.FeatureProviders.Add(new AutoAPIControllerFeatureProvider());
            });

            builder.Services.Configure<MvcOptions>(options =>
            {
                options.Conventions.Add(new AutoAPIApplicationModelConvention());
            });

            return builder;
        }

        public static IMvcCoreBuilder AddDynamicWebApi(this IMvcCoreBuilder builder)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }

            builder.ConfigureApplicationPartManager(applicationPartManager =>
            {
                applicationPartManager.FeatureProviders.Add(new AutoAPIControllerFeatureProvider());
            });

            builder.Services.Configure<MvcOptions>(options =>
            {
                options.Conventions.Add(new AutoAPIApplicationModelConvention());
            });

            return builder;
        }
    }

随后将ConfigureServices中的
services.AddControllers();调整为
services.AddControllers().AddDynamicWebApi();

  1. 测试使用
public class AutoTestService : IAutoAPIService
    {
        public IEnumerable<int> Getx(int value)
        {
            yield return value;
        }

        public InDto PostX(InDto value)
        {
            return value;
        }
    }

5.效果

  1. 尾注
    也可以用IApplicationModelConvention来做一些特殊的事情,如根据Controller继承的特性来添加一些特殊的过滤器从而实现扩展

测试代码
https://files.cnblogs.com/files/ives/DynamicAPI.zip?t=1683815091&download=true

[参考]
[.Net Core后端架构实战【2-实现动态路由与Dynamic API】]https://www.cnblogs.com/zhangnever/p/17131504.html)
ASP.NET Core 奇淫技巧之动态WebApi
.net core 实现动态 Web API
.NET 7.0 架构实战 动态路由与Dynamic API
.net mvc core 关于项目中分离控制器
FeatureProviders
wpf 动态修改表头_【asp.net core】实现动态 Web API
5.1.6.1 控制器特性提供器
在Asp.Net Core中使用ModelConvention实现全局过滤器隔离
ASP.NET Core中为指定类添加WebApi服务功能

.NET Core POCOController在动态Web API中的应用

标签:actionName,return,var,controller,api,action,new,net,动态
From: https://www.cnblogs.com/ives/p/autoapi.html

相关文章

  • .net 动态api
    本文在webapi的基础上进行后续的扩展,也可以实现不依赖项目类型的模式,只需要添加webapi对应的Nuget即可。首先创建接口来识别动态API的实现类publicinterfaceIAutoAPIService{}ControllerFeatureProvider创建一个类继承ControllerFeatureProvider并重写IsContr......
  • HDFS文件读写初窥- Java API
    安装HDFS1)hadoop下载:https://hadoop.apache.org/releases.html2)本地安装:https://hadoop.apache.org/docs/r3.3.5/hadoop-project-dist/hadoop-common/SingleCluster.html3)修改配置:etc目录下存放了hadoop相关配置文件,这里要在本地部署伪分布式模式,需要修改以下两个文件:e......
  • 关于谷歌浏览器出现“错误代码:net::ERR_UNSAFE_PORT”的解决办法
    搭建项目时需要自己配置端口信息,但是有人搭建之后会出现如下情况  但是换用edge等浏览器没有问题,这是因为chorme浏览器有自己的默认非安全端口,若访问这些端口就会出现这个错误,并且所有采用chorme内核的浏览器都会这样。解决方案是更换自己项目的端口,这里列出所有chorme的......
  • SEDCN:Structure enhanced deep clustering network via a weighted neighbourhood a
    论文阅读08-SEDCN:Structureenhanceddeepclusteringnetworkviaaweightedneighbourhoodauto-encoder论文信息论文地址:Structureenhanceddeepclusteringnetworkviaaweightedneighbourhoodauto-encoder-ScienceDirect代码地址:m22453/sedcn-nn(github.com)1.......
  • c# 调用webapi的几种方式
     HttpHelper帮助类publicstaticclassHttphelper{//Post请求publicstaticstringPostResponse(stringurl,stringpostData,outstringstatusCode){stringresult=string.Empty;//设置Http的正文......
  • Visual Studio Connected Services 生成http api代码
    生成的代码将和接口对应的参数、返回值一一对应,本文底层使用的工具为NSwag.exe,其他可替代的方案还有AutoSet.exe1.配置连接2.配置生成的代码相关属性3.如果遇到报错考虑将version添加到生成的swagger.json文件中底层实际是调用NSwag.exe生成的代码生成的文件位置此......
  • 接口测试:Eolink Apikit 和 Postman 哪个更好用?
    很多做服务端开发的同学,应该基本都用过Postman来测试接口,虽然Postman能支撑日常工作,但是总感觉还是少了点什么,比如需要Swagger来维护接口文档,需要人肉发送接口变更通知。如今,国产的接口管理工具做得越来越好了,比如,EolinkApikit,一站式API协作平台。EolinkApikit和Post......
  • APIView执行流程(源码分析)、Request对象源码分析
    目录一、APIView执行流程——源码分析(难,了解)1.1基于APIView+JsonResponse编写接口1.2基于APIView+Response写接口1.3APIView的执行流程二、Request对象源码分析(难,了解)一、APIView执行流程——源码分析(难,了解)1.1基于APIView+JsonResponse编写接口#原来基于django原生的Vi......
  • oracle 开启tnsping trace、sqlnet trace 、event10257
    在sqlnet.ora文件中加入以下参数:TNSPING.TRACE_LEVEL=SUPPORTTNSPING.TRACE_DIRECTORY=d:\oracle\trace“tnsping”工具的预期用途仅仅是测试OracleNet别名中指定的数据库侦听器是up还是down。“tnsping”工具不打算用作OracleNet性能测量工具B.Sql*nettraceSE......
  • java代理模式及动态代理类
    1.     代理模式代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。代理模式一般涉及到的角色有:抽象角色:声明真实对象和代理对象的共同接口;代理角色:......