使用.NET6实现动态API

目录
  • 开发环境
  • 项目地址
  • 项目目标
  • 编码约定
  • 核心代码
  • 使用示例

ApiLite是基于.NET6直接将Service层生成动态api路由,可以不用添加Controller,支持模块插件化,在项目开发中能够提高工作效率,降低代码量。

开发环境

  • .NET SDK 6.0.100-rc.2.21505.57
  • VS2022 Preview 7.0

项目地址

GitHub: https://github.com/known/ApiLite

项目目标

  • 根据Service动态生成api
  • 支持自定义路由模板(通过Route特性定义)
  • 支持模块插件化
  • 支持不同模块,相同Service名称的路由(命名空间需要有3级以上,例如:Com.Mod.XXX)
  • 自动根据方法名称判断请求方式,Get开头的方法名为GET请求,其他为POST请求

编码约定

  • 模块类库必须包含继承IModule接口的类
  • 需要生成api的Service必须继承IService接口
  • GET请求的方法必须以Get开头

核心代码

主要是ApiFeatureProvider和ApiConvention这两个自定义类来动态生成api,ApiFeatureProvider继承ControllerFeatureProvider,覆写IsController方法,判断服务类型是否符合Controller。ApiConvention实现了IApplicationModelConvention接口,实现动态添加Action。下面是主要代码,完整代码请在GitHub上下载。

static class ServiceExtension
{
    internal static WebApplicationBuilder AddKApp(this WebApplicationBuilder builder, Action<AppOption>? action = null)
    {
        var option = new AppOption();
        action?.Invoke(option);
        ...
        AddDynamicApi(mvcBuilder, option);//添加动态api
        return builder;
    }

    private static void AddDynamicApi(IMvcBuilder builder, AppOption option)
    {
        builder.ConfigureApplicationPartManager(m =>
        {
            m.ApplicationParts.Add(new AssemblyPart(typeof(IService).Assembly));
            foreach (var item in option.Modules)
            {
                item.Initialize();//初始化模块
                //将模块添加到ApplicationParts,这样才能发现服务类
                var assembly = item.GetType().Assembly;
                m.ApplicationParts.Add(new AssemblyPart(assembly));
            }
            m.FeatureProviders.Add(new ApiFeatureProvider());
        });

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

//判断服务类型是否为Controller
class ApiFeatureProvider : ControllerFeatureProvider
{
    protected override bool IsController(TypeInfo typeInfo)
    {
        if (!typeof(IService).IsAssignableFrom(typeInfo) ||
            !typeInfo.IsPublic ||
            typeInfo.IsAbstract ||
            typeInfo.IsGenericType)
            return false;

        return true;
    }
}

class ApiConvention : IApplicationModelConvention
{
    public void Apply(ApplicationModel application)
    {
        foreach (var controller in application.Controllers)
        {
            var type = controller.ControllerType;
            if (typeof(IService).IsAssignableFrom(type))
            {
                ConfigureApiExplorer(controller);
                ConfigureSelector(controller);
            }
        }
    }

    ...

    //构造路由模板
    private string GetRouteTemplate(ActionModel action)
    {
        if (action.Attributes != null && action.Attributes.Count > 0)
        {
            foreach (var item in action.Attributes)
            {
                if (item is RouteAttribute attribute)
                {
                    return attribute.Path;//返回自定义路由
                }
            }
        }

        var routeTemplate = new StringBuilder();
        //routeTemplate.Append("api");
        var names = action.Controller.ControllerType.Namespace.Split('.');
        if (names.Length > 2)
        {
            //支持不同模块相同类名,添加命名空间模块名作前缀
            routeTemplate.Append(names[^2]);
        }

        // Controller
        var controllerName = action.Controller.ControllerName;
        if (controllerName.EndsWith("Service"))
            controllerName = controllerName[0..^7];

        routeTemplate.Append($"/{controllerName}");

        // Action
        var actionName = action.ActionName;
        if (actionName.EndsWith("Async"))
            actionName = actionName[..^"Async".Length];

        if (!string.IsNullOrEmpty(actionName))
            routeTemplate.Append($"/{actionName}");

        return routeTemplate.ToString();
    }
}

使用示例

KHost.Run(args, o =>
{
    o.Modules.Add(new TestModule());//添加模块
});

class TestModule : IModule
{
    public void Initialize()
    {
    }
}

public class TestService : IService
{
    public string GetName(string name)
    {
        return $"Hello {name}";
    }

    public string SaveData(string data)
    {
        return $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} {data}";
    }

    [Route("api/test")]
    public string GetCustMethod(string id)
    {
        return id;
    }
}

以上所述是小编给大家介绍的使用.NET6实现动态API,希望对大家有所帮助。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • 简单聊下.NET6 Minimal API的使用方式

    目录 前言 使用方式 几行代码构建Web程序 更改监听地址 日志操作 基础环境配置 主机相关设置 默认容器替换 中间件相关 请求处理 路由约束 模型绑定 绑定示例 自定义绑定 总结 前言 随着.Net6的发布,微软也改进了对之前ASP.NET Core构建方式,使用了新的Minimal API模式.之前默认的方式是需要在Startup中注册IOC和中间件相关,但是在Minimal API模式下你只需要简单的写几行代码就可以构建一个ASP.NET Core的Web应用,真可谓非常的简单,加之配合c

  • 使用.Net6中的WebApplication打造最小API

    .net6在preview4时给我们带来了一个新的API:WebApplication,通过这个API我们可以打造更小的轻量级API服务.今天我们来尝试一下如何使用WebApplication设计一个小型API服务系统. 环境准备 .NETSDK v6.0.0-preview.5.21355.2 Visual Studio 2022 Preview 首先看看原始版本的WebApplication,官方已经提供了样例模板,打开我们的vs2022,选择新建项目选择asp.net core empty

  • 使用.NET6实现动态API

    目录 开发环境 项目地址 项目目标 编码约定 核心代码 使用示例 ApiLite是基于.NET6直接将Service层生成动态api路由,可以不用添加Controller,支持模块插件化,在项目开发中能够提高工作效率,降低代码量. 开发环境 .NET SDK 6.0.100-rc.2.21505.57 VS2022 Preview 7.0 项目地址 GitHub: https://github.com/known/ApiLite 项目目标 根据Service动态生成api 支持自定义路由模板(通

  • ASP.NET Core奇淫技巧之动态WebApi的实现

    一.前言 接触到动态WebApi(Dynamic Web API)这个词的已有几年,是从ABP框架里面接触到的,当时便对ABP的这个技术很好奇,后面分析了一波,也尝试过从ABP剥离一个出来作为独立组件来使用,可是后来因与ABP依赖太多而放弃.十几天前朋友 熊猫 将这部分代码(我和他在搞事情)成功的从 ABP 中剥离出来并做了一个简单Demo扔给我,经过这么久(实在是太懒_)终于经过一些修改.添加功能.封装,现在已经能作为一个独立组件使用,项目开源在Github(https://github.co

  • React-intl 实现多语言的示例代码

    最近在项目中添加了语言国际化的功能. 语言国际化,也有人说成是语言本地化,其实就是为Web App添加多语言,我们的项目当前包含了中文版和英文版,按理来说『逐字替换』也不是多大事儿,但是,这么Low的做法,有钱途吗? 一开始的时候,我考虑的是传统的为整个项目添加config文件,根据不同的语言和地区,加载不同的config文件,就能够达到界面语言切换的目的.当然,也正是因为这个想法太过于幼稚,所以才被称为『一开始』的想法.语言的国际化不仅仅是将界面上的UI文字翻译成另一种语言,还包括了日期&时间

  • 如何不用安装python就能在.NET里调用Python库

    前言 Pythonnet这个屌爆的项目的出现,使得我们可以用一种新的方式,让C#可以和Python之间进行互操作.但是它的设置和部署可能有点问题,真的是这样吗? 本文我会介绍Python.Included这个项目,它不但优雅的解决了这个问题,并且让.NET开发者可以轻松愉快的让.NET与Python进行互操作.作为概念的证明,我将使用Numpy.Net进行展示,它是一个.NET标准库,它为Python的Numpy提供了一个强类型API,并且使用它并不需要在Windows上安装Python. 开发

  • c#使用dynamic类型优化反射的方法

    什么是dynamic类型? 微软给出的官方文档中这样解释:在通过 dynamic 类型实现的操作中,该类型的作用是绕过编译时类型检查. 改为在运行时解析这些操作. dynamic 类型简化了对 COM API(例如 Office Automation API).动态 API(例如 IronPython 库)和 HTML 文档对象模型 (DOM) 的访问.在大多数情况下,dynamic 类型与 object 类型的行为类似. 但是,如果操作包含 dynamic 类型的表达式,那么不会通过编译器对该

  • Tree-Shaking 机制快速掌握

    目录 写在前面 直奔主题 最后 写在前面 最近在读霍老师的<Vue.js设计与实现>,感觉收获很多,由于霍老师是官方Vue维护成员,会从很通俗易懂的角度去讲Vue的实现细节.而不是按照源码死讲解,很不错,推荐给大伙! 直奔主题 Tree-Shaking 的本质其实就是消除无用代码也就是dead code,减小打包后文件,不太清楚dead code概念的不用担心,下面会讲到.Tree-Shaking是打包构建工具常用的优化手段.在我们日常的开发最常使用的,可能就是ESM的使用,会触发默认的Tre

  • Spring MVC利用Swagger2如何构建动态RESTful API详解

    前言 本文主要给大家介绍了关于Spring MVC用Swagger2构建动态RESTful API的相关内容,当多终端(WEB/移动端)需要公用业务逻辑时,一般会构建 RESTful 风格的服务提供给多终端使用. 为了减少与对应终端开发团队频繁沟通成本,刚开始我们会创建一份 RESTful API 文档来记录所有接口细节. 但随着项目推进,这样做所暴露出来的问题也越来越严重. a. 接口众多,细节复杂(需考虑不同的 HTTP 请求类型.HTTP 头部信息.HTTP 请求内容..),高质量地创建这

  • 在 Laravel 中动态隐藏 API 字段的方法

    我最近在 Laravel Brasil 社区看到一个问题,结果比看起来更有趣.想象一下你有一个 UsersResource 用下面的实现: <?php namespace App\Http\Resources; use Illuminate\Http\Resources\Json\Resource; class UsersResource extends Resource { /** * Transform the resource into an array. * * @param \Illu

  • FluentMybatis实现mybatis动态sql拼装和fluent api语法

    目录 开始第一个例子: Hello World 新建演示用的数据库结构 创建数据库表对应的Entity类 运行测试来见证Fluent Mybatis的神奇 配置spring bean定义 使用Junit4和Spring-test来执行测试 开始第一个例子: Hello World 新建Java工程,设置maven依赖 新建maven工程,设置项目编译级别为Java8及以上,引入fluent mybatis依赖包. <dependencies> <!-- 引入fluent-mybatis

随机推荐