目录

什么是 WebApiEngine?

  WebApiEngine 是一个可用于动态 WebApi 生成的引擎,基于 .NET Core(包括 .NET 5、 .NET 6),用于解决前后端分离、微服务、异步 Web 请求场景下的 WebApi 的动态生成和管理,并全面兼容 Swagger。

开源地址

  WebApiEngine 完全开源,可商用。承载于 Senparc.CO2NET.WebApi 库,同属于 CO2NET 开源项目:

https://github.com/Senparc/Senparc.CO2NET

使用方法

  以下是 WebApiEngine 的使用方法,将以最原始的默认 .NET Core WebApi 模板项目作为基础进行构建,以便大家学习和亲手实践。

  首先,使用 Visual Stduio 或命令行创建原始项目。

选择 ASP.NET Core Web API 项目

  或使用命令行,免去创建项目的其他步骤:

dotnet new webapi

命令行创建项目模板

  项目创建完成后,已经默认包含了一个模拟气象数据查询的接口:

原始项目

小贴士:您可以使用 NET Core 3.1 或 .NET 5、.NET 6 进行开发,代码没有任何差别。

  运行后默认已经加载了 Swagger:

原始运行页面,为 Swagger 首页

  使用 Swagger 我们已经可以测试 API:

使用 Swagger 测试接口运行

  此处的 API 还是需要手写 API 才能完成,打开 WeatherForecastController.cs 可以看到初始化内容:

 1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 using System.Threading.Tasks;
7
8 namespace WebApiSample.Controllers
9 {
10 [ApiController]
11 [Route("[controller]")]
12 public class WeatherForecastController : ControllerBase
13 {
14 private static readonly string[] Summaries = new[]
15 {
16 "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
17 };
18
19 private readonly ILogger<WeatherForecastController> _logger;
20
21 public WeatherForecastController(ILogger<WeatherForecastController> logger)
22 {
23 _logger = logger;
24 }
25
26 [HttpGet]
27 public IEnumerable<WeatherForecast> Get()
28 {
29 var rng = new Random();
30 return Enumerable.Range(1, 5).Select(index => new WeatherForecast
31 {
32 Date = DateTime.Now.AddDays(index),
33 TemperatureC = rng.Next(-20, 55),
34 Summary = Summaries[rng.Next(Summaries.Length)]
35 }).ToArray();
36 }
37 }
38 }

  上述代码是在 Controller 里面直接演示了逻辑代码(包括数据查询),更多的情况,我们会把这些逻辑封装在 Service 中,并由 Controller 调用。如,创建 WeatherService.cs:

 1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4
5 namespace WebApiSample
6 {
7 public class WeatherService
8 {
9 private static readonly string[] Summaries = new[]
10 {
11 "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
12 };
13
14 public IEnumerable<WeatherForecast> GetWeatherForecasts()
15 {
16 var rng = new Random();
17 return Enumerable.Range(1, 5).Select(index => new WeatherForecast
18 {
19 Date = DateTime.Now.AddDays(index),
20 TemperatureC = rng.Next(-20, 55),
21 Summary = Summaries[rng.Next(Summaries.Length)]
22 })
23 .ToArray();
24 }
25 }
26 }

  修改 WeatherForecastController.cs:

 1 using Microsoft.AspNetCore.Mvc;
2 using System.Collections.Generic;
3
4 namespace WebApiSample.Controllers
5 {
6 [ApiController]
7 [Route("[controller]")]
8 public class WeatherForecastController : ControllerBase
9 {
10 private readonly WeatherService _weatherService;
11
12 public WeatherForecastController(WeatherService weatherService)
13 {
14 this._weatherService = weatherService;
15 }
16
17 [HttpGet]
18 public IEnumerable<WeatherForecast> Get()
19 {
20 return _weatherService.GetWeatherForecasts();
21 }
22 }
23 }

  

  注意:如果像上述代码 12 行中那样,使用构造函数注入 WeatherService,需要在 Startup.cs 中添加:

services.AddScoped<WeatherService>();

  当我们在粒度越来越小的微服务、前后端分离的场景下进行开发和迭代,会发现 API 的数量会几何级数地上升。

  此时为了能让 Service 中的逻辑方法毫无变化地传递给客户端,需要做大量 API 创建的重复劳动,维护也会越来越混乱。

越来越复杂、混乱的 API 导致了大量低效、低价值的重复劳动

  为了解决这样的问题,WebApiEngine 登场了! 让我们来看看 WebApiEngine 能做什么?

使用 [ApiBind] 标签让任何方法变成 WebApi

  我们在 WeatherService 下再创建一个名为 GetWeatherForecast 的新方法,并附加一个 int 类型参数,用于演示新的接口:

 1         public WeatherForecast GetWeatherForecast(int index)
2 {
3 var rng = new Random();
4 return new WeatherForecast
5 {
6 Date = DateTime.Now.AddDays(index),
7 TemperatureC = rng.Next(-20, 55),
8 Summary = Summaries[rng.Next(Summaries.Length)]
9 };
10 }

  然后,通过加单的 3 步,完成动态 API 的实现:

  第一步:安装 Senparc.CO2NET.WebApi 包:

安装 Senparc.CO2NET.WebApi 包

  也可以在项目目录下,使用命令行添加:

dotnet add package Senarc.CO2NET.WebApi

   第二步:在 ConfigureServices() 方法中添加两行代码:

1             var builder = services.AddMvcCore().AddApiExplorer();
2 services.AddAndInitDynamicApi(builder, null);

  第三步:添加 [ApiBind] 标签

  在任意方法上添加 [ApiBind] 标签,如之前创建的 GetWeatherForecast(int index) 方法:

 1         [ApiBind]
2 public WeatherForecast GetWeatherForecast(int index)
3 {
4 var rng = new Random();
5 return new WeatherForecast
6 {
7 Date = DateTime.Now.AddDays(index),
8 TemperatureC = rng.Next(-20, 55),
9 Summary = Summaries[rng.Next(Summaries.Length)]
10 };
11 }

  完成!

  重新启动项目,即可看到新的 GetWeatherForecast 接口:

 
Swagger 首页,显示新接口 测试执行

  上述我们只添加了 3 行代码(如果项目本身就需要 services.AddMvcCore(),则只需要 2 行),我们便完成了让任何一个方法开放为接口的能力!

小贴士

1、您可以试一下静态方法,同样有效!

2、细心的开发者已经发现,自动生成的默认请求动作为 Post,我们可以通过修改全局配置修改默认动作,如:

1 services.AddAndInitDynamicApi(builder, null, ApiRequestMethod.Get);

对 API 进行分类

  有时候,为了方便 API 的管理,我们会对 API 的路径进行分类,甚至在模块化、插件化的框架下,同一个功能模块可能会由不同的程序集(或 dll)来支持,这时候怎么让不同“产地”的 API 进行充分“重组”呢?

  我们只需要对 API 进行分类(Category)参数的设置,例如,在上述 ApiBind 特性中添加参数:

 
特性标签添加 Category 参数 成功合并到 WeatherForecast 分类

自定义 API 名称

  上述路径默认包含(暴露)了 GetWeatherForecast 方法所属的类,有时我们甚至需要将多个不同类下面的方法,整合到同一个路径前缀下,这种情况下,可以继续定义 ApiBind 的 Name 参数,使其拥有自定义的路径前缀:

特性标签设置 Name 参数 配置完全可控的路径前缀

小贴士

为了防止接口名称重合和便于直观定位,接口路径最后一段命名(WeatherForecast_MyApi)目前不可设置,规则为:<类名>_<方法名>。

当然如果真的出现重名,WebApiEngine 也会自动修改。

测试:我们添加一个新的类 WeatherService2,并且标记一个具有相同 Category 和 Name 值的方法:

1     public class WeatherService2
2 {
3 [ApiBind("WeatherForecast", "MyApi")]
4 public string GetWeatherForecast(string str)
5 {
6 return "the parameter value is :" + str;
7 }
8 }

运行结果:

WebApiEngine 会自动处理重名的 API

复制特性

  动态 API 的另外一个难点是,正常的 WebAPI 通常都需要定义自己的特性,如访问鉴权、行为过滤,等等。WebApiEngine可以将原始方法上的特性标签直接复制到动态 API 上。

  我们在 GetWeatherForecast 方法上添加权限验证特性:

 1         [ApiBind("WeatherForecast", "MyApi")]
2 [Authorize]
3 public WeatherForecast GetWeatherForecast(int index)
4 {
5 var rng = new Random();
6 return new WeatherForecast
7 {
8 Date = DateTime.Now.AddDays(index),
9 TemperatureC = rng.Next(-20, 55),
10 Summary = Summaries[rng.Next(Summaries.Length)]
11 };
12 }

  然后运行接口:

[Authorize] 标签生效

  上面的测试可以看到 [Authorize] 标签已经生效(虽然提示了 Authorize 配置错误,是因为我们没有进行授权配置)。

  WebApiEngine 支持所有的特性标签。

为整个类配置 WebApi

  除了在某个具体的方法上添加 [ApiBind] 特性标签,您还可以在类(class)上使用此特性,使下属所有的方法(包括静态方法)都拥有相同的配置。

  class 上的特性标签同样会自动配置,其规则如下:

  1. 如果 class 设置了特性标签(如 [Authorize]),则下属所有的方法也将继承对应特性;
  2. 如果下属方法具有和 class 一样的特性标签,将完全覆盖 class 的特性设置;
  3. 集成特性标签的顺序,为先按顺序添加 class 的标签,后按顺序添加方法的标签(注此顺序为 CustomAttributeData.GetCustomAttributes() 获得到的顺序);

测试:

  将之前的 WeatherService2 类进行重写:

 1     [ApiBind("ClassCoverAttribute", "MyApi")]
2 public class WeatherService2
3 {
4 public string GetWeatherForecast(string str)
5 {
6 return "the parameter value is :" + str;
7 }
8
9 [ApiBind(ApiRequestMethod = ApiRequestMethod.Get)]
10 public string GetWeatherForecastCopy(string str)
11 {
12 return "the parameter value is :" + str;
13 }
14
15 public static string GetWeatherForecastCopyStatic(string str)
16 {
17 return "[static method]the parameter value is :" + str;
18 }
19 }

  第 1 行代码在 class 上进行添加,使其中 2 个方法都生效。

  第 9 行代码改写了 ApiBind 标签,使默认的 Post 方法,改为了 Get 方法。

  第 10 行代码是一个静态方法,同样能“享受”整个 class 的配置(当然也支持使用自定义 [ApiBind],然后覆盖 class 的配置)。

运行结果:

  运行结果中:

  • ① 是 GetWeatherForecast() 方法
  • ② 是 GetWeatherForecastCopyStatic() 静态方法(因为使用了 class继承,因此默认为相同的名称,后续版本将升级为当前方法名称)
  • ③ 是 WeatherService 类中的演示方法,和当前类无关
  • ④ 是 GetWeatherForecastCopy() 方法,该方法的 [ApiBind] 特性覆盖了 class 上的特性,因此没有指定 Category,使用了默认的分类名称,即当前程序集名称

忽略某些特定的方法

  有时,虽然我们偷懒将某个 class 一次性标记为 [ApiBind],但也会有个别的方法,我们并不希望开放为 API,这时候,可以使用 WebApiEngine 提供的忽略方法。

  有两种方式可以做到。

  方式一:使用 IgnoreApiBind 特性,如:

1         [IgnoreApiBind]
2 public static string GetWeatherForecastCopyStatic(string str)
3 {
4 return "[static method]the parameter value is :" + str;
5 }

  方式二:设置 ApiBind 特性中的 Ignore 属性,如:

1         [ApiBind(Ignore = true)]
2 public static string GetWeatherForecastCopyStatic(string str)
3 {
4 return "[static method]the parameter value is :" + str;
5 }

忽略某些特定的分类

  通过配置,我们也可以忽略部分特定的分类(Category),在运行引擎之前,在 startup.cs 中进行定义:

1             Senparc.CO2NET.WebApi.Register.AddOmitCategory("WeatherForecast");
2
3 var builder = services.AddMvcCore().AddApiExplorer();
4 services.AddAndInitDynamicApi(builder, null);

  只需添加上述第 1 行代码,即可忽略整个 WeatherForecast 分类的接口(当然不能忽略通过原始方法编写的 Controller 内的 API):

 
忽略前 忽略后

进阶

  请关注后续内容:

  • 《1 行代码,将任何一个方法开放为 WebApi》
  • 《1 行代码,配置 WebApi 的注释文档》
  • 《0 代码,结合 NeuCharFramework 高效使用 WebApiEngine》
  • 《使用 WebApiEngine 实现微信接口可视化测试环境》
  • 《使用 NeuCharFramework + WebApiEngine 搭建微信全平台接口代理服务器》
  • 等等

本文示例源码下载

https://github.com/JeffreySu/WebApiEngineSample

[本系列未完待续,持续更新中]

动态 WebApi 引擎使用教程(3行代码完成动态 WebApi 构建)的更多相关文章

  1. JavaScript模板引擎原理,几行代码的事儿

    一.前言 什么是模板引擎,说的简单点,就是一个字符串中有几个变量待定.比如: var tpl = 'Hei, my name is <%name%>, and I\'m <%age% ...

  2. Python爬虫教程(16行代码爬百度)

    最近在学习python,不过有一个正则表达式一直搞不懂,自己直接使用最笨的方法写出了一个百度爬虫,只有短短16行代码.首先安装必背包: pip3 install bs4 pip3 install re ...

  3. jQuery动态添加删除与添加表行代码

    具体实现代码如下: table的HTML如下: 代码如下 复制代码 <input type="button" value="添加一行" />< ...

  4. 动态横向(水平)合并Repeater数据行DataItem的列

    Insus.NET有对GridView控件进行横纵分别合并列:横:<动态横向(水平)合并GridView数据行DataRow的列>http://www.cnblogs.com/insus/ ...

  5. JAE京东云引擎Git上传管理代码教程和京东云数据库导入导出管理

    文章目录 Git管理准备工作 Git工具上传代码 发布代码装程序 mywebsql管理 京东云引擎小结   JAE京东云引擎是京东推出的支持Java.Ruby.Python.PHP.Node.js多语 ...

  6. 《zw版·Halcon-delphi系列原创教程》 只有2行代码的超市收款单ocr脚本

    <zw版·Halcon-delphi系列原创教程> 只有2行代码的超市收款单ocr脚本只有2行代码的超市收款单ocr脚本 发了这么多教程,有网友问,为什么没有ocr的.      的确,在 ...

  7. 《zw版·Halcon-delphi系列原创教程》简单的令人发指,只有10行代码的车牌识别脚本

    <zw版·Halcon-delphi系列原创教程>简单的令人发指,只有10行代码的车牌识别脚本 简单的令人发指,只有10行代码的车牌识别脚本      人脸识别.车牌识别是opencv当中 ...

  8. JS模版引擎[20行代码实现模版引擎读后感]

    曾经阅读过<只有20行JAVASCRIPT代码, 手把手教你写一个页面模版引擎>这篇文章, 对其中实现模版的想法实在膜拜, 于是有了这篇读后感, 谈谈自己对模版引擎的理解, 以及用自己的语 ...

  9. 使用PyQt(Python+Qt)+动态编译36行代码实现的计算器

    PyQt是基于跨平台的图形界面C++开发工具Qt加Python包装的一个GPL软件(GPL是GNU General Public License的缩写,是GNU通用公共授权非正式的中文翻译),Qt基于 ...

随机推荐

  1. 『言善信』Fiddler工具 — 7、统计选项页详解(Statistics)

    目录 1.Statistics选项页介绍 2.Statistics界面内容说明 3.Statistics选项页底部图表说明 1.Statistics选项页介绍 Statistics 页签显示当前用户选 ...

  2. 题解-P3810

    P3810 [模板]三维偏序(陌上花开) 更好的阅读体验1 更好的阅读体验2 前置算法 树状数组求逆序对 归并排序求逆序对 解题之前,让我们来看一看弱化版本 \(\to\) 二维偏序 题意 给定两个长 ...

  3. 一起来踩踩 Spring 中这个循环依赖的坑

    1. 前言 2. 典型场景 3. 什么是依赖 4. 什么是依赖调解 5. 为什么要依赖注入 6. Spring的依赖注入模型 7. 非典型问题 参考资料 1. 前言 这两天工作遇到了一个挺有意思的Sp ...

  4. expdp数据泵导出数据汇总

    [oracle@enmo1 ~]$ mkdir datadump[oracle@enmo1 ~]$ cd datadump/[oracle@enmo1 datadump]$ pwd/home/orac ...

  5. Etcd中linearizable read实现

    linearizable 有点疑惑,不确定是现在浏览的版本没开发完全,还是没有按照论文的linearizable来实现. 按照论文所说,在客户端请求的时候,实际上是一个强一致的 exactly onc ...

  6. 数据权限筛选(RLS)的两种实现介绍

    在应用程序中,尤其是在统计的时候, 需要使用数据权限来筛选数据行. 简单的说,张三看张三部门的数据, 李四看李四部门的数据:或者员工只能看自己的数据, 经理可以看部门的数据.这个在微软的文档中叫Row ...

  7. 11、ssh自动化脚本编写

    11.1.如何一键自动化安装50台规模集群网站搭建: 1.自动化思路: kickstart无人值守安装linux系统(自动创建用户.密码.优化): 分发机上创建秘钥对.批量发走公钥(expect): ...

  8. Python分析【公众号】历史评论,看看大家的留言情况!

    大家好,我是辰哥~~~ 辰哥玩公众号有一段时间了,这期文章分析一波读者的留言情况,不仅可以对公众号的各位铁粉一目了然,还可以通过分析的结果对公众号的经营进行更好的规划.如读者留言的内容通常是内容是什么 ...

  9. 面试:MyBatis面试总结

    1.什么是Mybatis? (1)Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动.创建连接.创建statement ...

  10. Android java层常见加密算法的hook自吐以及栈信息的打印

    杂谈:其实原理并没有很难,本质就是hook Android的框架层中的api将我们想要的key和iv(也可以没有,就打个比方),但是目前的话,很多厂家已经不在直接调用java层的这些加密算法的api了 ...