自己造轮子是一件苦差事。 现在,您可以专注于业务开发,仅需集成 ⭐️Furion⭐️ 即可。
Skip to main content

5. 动态 WebAPI

小知识

动态WebAPI 实际上就是将普通的类变为 Controller,也就是 动态WebAPI 就是控制器,支持控制器一切功能。

5.1 什么是控制器

简单来说,控制器是一个承上启下的作用,根据用户输入,执行响应行为(动作方法),同时在行为中调用模型的业务逻辑,返回给用户结果(视图)。

ASP.NET Core 中,控制器有两种表现形式:

  • Mvc(带视图)
  • WebAPI(RESTful API)
using Microsoft.AspNetCore.Mvc;

namespace Furion.Web.Entry.Controllers
{
public class MvcController : Controller
{
public IActionResult Index()
{
return View();
}
}
}

Mvc 控制器和 WebAPI 控制器最大的区别是 WebAPI 控制器不带 视图 和通过 请求谓词和路由地址响应行为

5.2 Mvc 控制器 约定和缺点

在学习动态 WebAPI 控制器之前,首先了解 ASP.NET CoreWebAPI 的一些约定和注意事项。

5.2.1 WebAPI 约定

ASP.NET Core 应用中,一个 WebAPI 控制器需遵循以下约定:

  • 控制器类必须继承 ControllerBase 或间接继承
  • 动作方法必须贴有 [HttpMethod] 特性,如:[HttpGet]
  • 控制器或动作方法至少有一个配置 [Route] 特性
  • 生成 WebAPI 路由地址时会自动去掉控制器名称 Controller 后缀,同时也会去掉动作方法匹配的 HttpVerb 谓词,如 GET,POST,DELETE,PUT
  • 不支持返回非 IEnumerable<T> 泛型对象
  • 不支持类类型参数在 GET,HEAD 请求下生成 Query 参数

除了上述约定外,WebAPI 路由地址基本靠手工完成,不利于书写,不利于维护,再者,在移动应用对接中难以进行多版本控制。

5.2.2 .NET Core WebAPI 缺点

通过上一章节可以看出,ASP.NET Core 应用实现 WebAPI 需要遵循种种约定,而且容易出错。

除了这些约定,.NET Core WebAPI 有以下缺点:

  • 路由地址基本靠手工完成
  • 在现在移动为王的时代,不利于进行多版本控制
  • 对接 Swagger 文档分组比较复杂
  • 实现 Policy 策略授权也比较复杂
  • 不支持控制器热插拔插件化
  • 难以实现复杂自定义的 RESTful API 风格

5.3 动态 WebAPI 控制器

针对以上 ASP.NET Core 提供的 WebAPI 必须遵循的约定和不可避免的缺点,Furion 框架创造出一种更加灵活创建 WebAPI 控制器的方式。

这个方式在继承了 ASP.NET Core WebAPI 所有优点,同时进行了大量拓展和优化。优化后的 WebAPI 具有以下优点:

  • 具备原有的 ControllerBase 所有功能
  • 支持任意公开 非静态 非抽象 非泛型类转控制器
  • 提供更加灵活方便的 IDynamicApiController 空接口或 [DynamicApiController] 特性替代 ControllerBase 抽象类
  • 无需手动配置 [HttpMethod] 特性,同时支持一个动作方法多个 HttpVerb
  • 无需手动配置 [Route] 特性,支持更加灵活的配置及自动路由生成
  • 支持返回泛型接口,泛型类
  • Swagger 深度结合,提供极其方便的创建 Swagger 分组配置
  • 支持 Basic Auth,Jwt,ApiKey 等多种权限灵活配置
  • 支持控制器、动作方法版本控制功能
  • 支持 GET、HEAD 请求自动转换 类类型参数
  • 支持生成 OAS3 接口规范

5.4 注册动态 WebAPI 服务

备注

.AddDynamicApiControllers() 默认已经集成在 AddInject() 中了,无需再次注册。也就是下列代码可不配置。

Furion.Web.Core\FurWebCoreStartup.cs
using Microsoft.Extensions.DependencyInjection;

namespace Furion.Web.Core
{
[AppStartup(800)]
public sealed class FurWebCoreStartup : AppStartup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddDynamicApiControllers();
}
}
}
特别注意

.AddDynamicApiControllers() 必须在 services.AddControllers() 之后注册。

5.5 第一个例子

创建一个 FurionAppService 类继承 IDynamicApiController 接口 或 贴 [DynamicApiController] 特性,并在这个类中编写一个 Get 方法。

  • IDynamicApiController 方式
using Furion.DynamicApiController;

namespace Furion.Application
{
public class FurionAppService : IDynamicApiController
{
public string Get()
{
return $"Hello {nameof(Furion)}";
}
}
}
  • [DynamicApiController] 方式
using Furion.DynamicApiController;

namespace Furion.Application
{
[DynamicApiController]
public class FurionAppService
{
public string Get()
{
return $"Hello {nameof(Furion)}";
}
}
}

如下图所示,一个 WebAPI 接口就这么生成了。

5.6 动态 WebAPI 原理解析

5.6.1 控制器特性提供器

Furion 框架会在应用启动时注册 DynamicApiControllerFeatureProvider 控制器特性提供器,该提供器继承自 ControllerFeatureProvider 类。

接着重写 bool IsController(TypeInfo typeInfo) 方法,用来标识控制器类型。在 Furion 框架中,继承自 ControllerBase 类或 IDynamicApiController 接口或 [DynamicApiController] 特性都会被标记为控制器类型。

5.6.2 应用模型转换器

Furion 框架同时在应用启动时注册 DynamicApiControllerApplicationModelConvention 应用模型转换器,该转换器继承自 IApplicationModelConvention 接口。

接着实现 void Apply(ApplicationModel application) 接口方法。在该方法中配置控制器名称、路由、导出可见性及动作方法名称、路由、导出可见性等。

实际上该方法做的就是按照 WebAPI 约定 提前帮我们配置好路由、请求谓词等信息。避免了手动配置的同时还增加了许多新特性,如版本控制。

5.7 动态 WebAPI 配置约定

5.7.1 控制器默认约定

  • 生成控制器名称默认去除以 AppServices,AppService,ApiController,Controller,Services,Service 作为前后缀的字符串。见第一个例子中的 FurionAppService -> Furion 支持自定义配置
  • 控制器名称带 V[0-9_] 结尾的,会自动生成控制器版本号,如 FurionAppServiceV2 -> Furion@2FurionAppServiceV1_1_0 -> Furion@1.1.0支持版本分隔符配置
  • 控制名称以 骆驼命名(CamelCase) 会自动切割成多个单词 - 连接。支持自定义配置

5.7.2 动作方法默认约定

  • 生成的动作方法名称默认去除以 Post/Add/Create/Insert/Submit,GetAll/GetList/Get/Find/Fetch/Query/Search,Put/Update,Delete/Remove/Clear,Patch 开头的字符串。支持自定义配置
  • 生成的动作方法名称默认去除以 Async 作为前后缀的字符串。支持自定义配置
  • 动作方法名称带 V[0-9_] 结尾的,会自动生成动作方法版本号,如 ChangePasswordV2 -> ChangePassword@2ChangePasswordV1_1_0 -> ChangePassword@1.1.0支持版本分隔符配置
  • 动作方法名称以 骆驼(驼峰)/帕斯卡命名(CamelCase/Pascal) 会自动切割成多个单词 - 连接。支持自定义配置
  • 动作方法参数将自动转为小写。支持自定义配置

5.7.3 请求谓词默认约定

  • 动作方法名
    • Post/Add/Create/Insert/Submit/Change 开头,则添加 [HttpPost] 特性。
    • GetAll/GetList/Get/Find/Fetch/Query 开头,则添加 [HttpGet] 特性。
    • Put/Update 开头,则添加 [HttpPut] 特性。
    • Delete/Remove/Clear 开头,则添加 [HttpDelete] 特性。
    • Patch 开头,则添加 [HttpPatch] 特性
    • 支持自定义配置
  • 如果不在上面约定中,则默认添加 [HttpPost] 特性。支持自定义配置

5.7.4 路由地址默认约定

  • 默认以 api 开头。支持自定义配置
  • 默认转换为小写路由地址。支持自定义配置
  • 生成控制器路由模板格式为:api/前置参数列表/模块名或默认区域名/[controller@版本号]/后置参数列表
  • 生成动作方法路由模板格式为:前置参数列表/模块名/[action@版本号]/后置参数列表

5.7.5 其他约定

  • 默认不处理 ControllerBase 控制器类型。支持自定义配置
  • 默认不处理 GET,HEAD 请求的引用类型参数。支持自定义配置

5.8 更多例子

5.8.1 多种请求谓词方法

using Furion.DynamicApiController;

namespace Furion.Application
{
public class FurionAppService : IDynamicApiController
{
public string Get()
{
return $"GET 请求";
}

public string Post()
{
return $"POST 请求";
}

public string Delete()
{
return $"DELETE 请求";
}

public string Put()
{
return $"PUT 请求";
}

public string Patch()
{
return $"PATCH 请求";
}
}
}

如下图所示:

5.8.2 多个自定义动作方法

using Furion.DynamicApiController;

namespace Furion.Application
{
public class FurionAppService : IDynamicApiController
{
public string GetVersion()
{
return $"v1.0.0";
}

public string ChangeProfile()
{
return "修改成功";
}

public string DeleteUser()
{
return "删除成功";
}
}
}

如下图所示:

5.8.3 带参数动作方法

using Furion.DynamicApiController;

namespace Furion.Application
{
public class FurionAppService : IDynamicApiController
{
public string GetUser(int id)
{
return $"{id}";
}

public string GetUser(int id, string name)
{
return $"{id} {name}";
}

public TestDto Add(TestDto testDto)
{
return testDto;
}
}
}

如下图所示:

5.8.4 GET/HEAD 类类型参数

默认情况下,ASP.NET Core 会将 GET/HEAD 请求中的 类类型参数 设置为 [FromBody] 绑定,如:

using Furion.DynamicApiController;

namespace Furion.Application
{
public class FurionAppService : IDynamicApiController
{
public TestDto GetTest(TestDto testDto)
{
return testDto;
}
}
}

如下图所示:

但是,GET、HEAD 请求不支持 From Body 绑定。所以我们需要转换为 Query 查询参数。

Furion 框架支持以下两种方式配置:

using Furion.DynamicApiController;
using Microsoft.AspNetCore.Mvc;

namespace Furion.Application
{
public class FurionAppService : IDynamicApiController
{
public TestDto GetTest([FromQuery] TestDto testDto)
{
return testDto;
}
}
}

如下图所示:

5.8.5 自定义参数位置

Furion 框架提供了非常方便的自定义参数位置的特性 [ApiSeat],通过 [ApiSeat] 可配置参数位置,支持以下四种位置:

  • ApiSeats.ControllerStart:控制器之前
  • ApiSeats.ControllerEnd:控制器之后
  • ApiSeats.ActionStart:动作方法之前
  • ApiSeats.ActionEnd:动作方法之后。默认值
using Furion.DynamicApiController;
using System;

namespace Furion.Application
{
public class FurionAppService : IDynamicApiController
{
// 参数默认为 ApiSeats.ActionEnd
public string RouteSeat(int id, string name)
{
return "配置路由参数位置";
}

public string RouteSeat(
[ApiSeat(ApiSeats.ControllerStart)] int id, // 控制器名称之前
[ApiSeat(ApiSeats.ControllerEnd)] string name, // 控制器名称之后
[ApiSeat(ApiSeats.ControllerEnd)] int age, // 控制器名称之后
[ApiSeat(ApiSeats.ActionStart)] decimal weight, // 动作方法名称之前
[ApiSeat(ApiSeats.ActionStart)] float height, // 动作方法名称之前
[ApiSeat(ApiSeats.ActionEnd)] DateTime birthday) // 动作方法名称之后(默认值)
{
return "配置路由参数位置";
}
}
}

如下图所示:

温馨提示

多个 同位置 配置的参数将按照 定义参数顺序 进行排序。

特别注意

[ApiSeat] 只能应用于贴了 [FromRoute] 特性的参数或 基元类型、值类型、可空基元类型和可空值类型

5.8.6 自定义请求谓词

using Furion.DynamicApiController;
using Microsoft.AspNetCore.Mvc;

namespace Furion.Application
{
public class FurionAppService : IDynamicApiController
{
[HttpPost]
public string GetVersion()
{
return "1.0.0";
}
}
}

如下图所示:

5.8.7 支持多个谓词

using Furion.DynamicApiController;
using Microsoft.AspNetCore.Mvc;

namespace Furion.Application
{
public class FurionAppService : IDynamicApiController
{
[HttpPost, HttpGet, AcceptVerbs("PUT", "DELETE")]
public string GetVersion()
{
return "1.0.0";
}
}
}

如下图所示:

特别注意

如果动作方法中含有 类类型参数,且含有 POST/PUT/DELETE 任意请求谓词,那么该参数会自动添加 [FromBody] 参数,即使在 GET/HEAD 请求中不支持。

5.8.8 支持自定义路由

支持控制器和动作方法自定义路由:

using Furion.DynamicApiController;
using Microsoft.AspNetCore.Mvc;

namespace Furion.Application
{
[Route("customapi/mobile/[controller]")]
public class FurionAppService : IDynamicApiController
{
public string GetVersion()
{
return "1.0.0";
}
}
}

如下图所示:

小提示

动作方法自定义路由如果以 / 开头,则不会合并控制器路由。

推荐配置

自定义路由如果需要用到 控制器/动作方法名称,推荐使用 [controller][action] 占位符,因为该占位符已经自动处理了 前后缀、版本号、模块名称等。

5.8.9 多路由随意组合

Furion 框架提供了非常灵活的各种路由组合方式,支持一对多,多对多路由组合:

using Furion.DynamicApiController;
using Microsoft.AspNetCore.Mvc;

namespace Furion.Application
{
[Route("api/[controller]")]
[Route("api/[controller]/second")]
[Route("api/[controller]/three")]
public class FurionAppService : IDynamicApiController
{
[HttpGet]
[HttpGet("get/[action]")]
[HttpPost]
[HttpPost("post/cus-version")]
public string GetVersion()
{
return "1.0.0";
}
}
}

如下图所示:

特别注意

动作方法不能同时贴 [Route][HttpMethod] 特性,只能二取一。

5.8.10 支持版本控制

using Furion.DynamicApiController;

namespace Furion.Application
{
public class FurionAppServiceV1 : IDynamicApiController
{
public string Get()
{
return nameof(Furion);
}
}

public class FurionAppServiceV1_2 : IDynamicApiController
{
public string Get()
{
return nameof(Furion);
}
}

public class FurionAppServiceV1_2_1 : IDynamicApiController
{
public string Get()
{
return nameof(Furion);
}
}
}

如下图所示:

版本生成原理

V[0-9_] 结尾的命名自动解析成版本号,如 FurionAppServiceV2 -> Furion@2

版本复写

除了通过特定后缀方式以外,版本还直接通过 [ApiDescriptionSettings] 进行复写。如:

[ApiDescriptionSettings(Version = "4.0")]
public string GetV1()
{
return nameof(Furion);
}

这时,生成版本将采用 4.0 替代 1

5.8.11 不公开控制器或动作方法

有些时候,我们无需导出某个动作方法或控制器(不显示到 Swagger),只需要添加 [ApiDescriptionSettings(false)][ApiExplorerSettings(IgnoreApi = true)]即可。

另外动作方法还支持 [NonAction] 标记不是一个有效的控制器或 Action。

推荐使用

推荐控制器或动作方法设置不导出使用 [ApiDescriptionSettings(false)] 特性。该特性默认继承自 ApiExplorerSettingsAttribute 类。

5.8.12 保持控制器和方法命名

默认情况下,动态 API 会将控制器和方法名输出为 RESTFul 风格的路由,如需保留原有设计,只需配置:

{
"DynamicApiControllerSettings": {
"KeepName": true,
"KeepVerb": true,
"LowercaseRoute": false
}
}

5.8.13 方法参数 [FromQuery] 化/参数非必填/参数可选

默认情况下,所有的基元类型参数都会贴上 [FromRoute] 特性,如果需要将参数调整为 [FromQuery] 修饰,只需要在方法上面贴 [QueryParameters] 特性即可,如:

[QueryParameters]
public string Get(int id, string name)
{
return nameof($"{id} {name}");
}

生成的路由为:https://xxx.com?id=1&name=Furion

如果不喜欢每个都配置,也可以全局配置(只会影响基元类型的参数):

{
"DynamicApiControllerSettings": {
"UrlParameterization": true
}
}
特别注意

贴了 [QueryParameters] 之后,会对所有参数影响,包括类类型参数,如果不需要处理某个参数,只需要贴 [FromXXX] 特性即可。

5.8.14 参数绑定配置

Furion 框架提供了多种参数特性配置参数绑定规则:

  • [FromRoute]:通过路由参数绑定值
  • [FromQuery]:通过 Url 地址参数绑定值
  • [FromBody]:通过 Request Body 参数绑定值
  • [FromForm]:通过表单提交绑定值
  • [FromHeader]:通过 Request Header 参数绑定值

5.8.15 自定义根据方法名生成 [HttpMethod] 规则

Furion 框架中,在没有配置 [HttpMethod] 特性的情况下,会自动根据方法名第一个参数进行分析,并生成对应的 [HttpMethod] 特性,规则如下:

  • 动作方法名
    • Post/Add/Create/Insert/Submit 开头,则添加 [HttpPost] 特性。
    • GetAll/GetList/Get/Find/Fetch/Query 开头,则添加 [HttpGet] 特性。
    • Put/Update 开头,则添加 [HttpPut] 特性。
    • Delete/Remove/Clear 开头,则添加 [HttpDelete] 特性。
    • Patch 开头,则添加 [HttpPatch] 特性
    • Head 开头,则添加 [HttpHead] 特性
    • 支持自定义配置
  • 如果不在上面约定中,则默认添加 [HttpPost] 特性。支持自定义配置

但是,有些时候这不是我们想要的规则,这时我们只需要在 appsettings.json 中配置即可:

{
"DynamicApiControllerSettings": {
"VerbToHttpMethods": [
["getall", "HEAD"], // => getall 会被复写为 `[HttpHead]`
["other", "PUT"] // => 新增一条新规则,比如,一 `[other]` 开头会转换为 `[HttpPut]` 请求
]
}
}
特别注意

二维数组中的每一个元素的第一个元素必须是全小写,第二个元素必须是全大写大写,第二个元素取值有:HEAD, GET, PUT, POST, PATCH, DELETE

5.8.16 路由参数非必填/选填

Furion v2.8.6 版本中实现了 [FromRoute] 参数非必填功能,支持以下几种方式:

// 方式一,通过可空 ?
public object Method1(int id, Datetime? dateTime)
{
}

// 方式二,通过默认值
public object Method1(int id, int age = 10)
{
}

// 方式三,默认值 + 可空 ?
public object Method1(int id, int? age = 10)
{
}

// 方式四,[FromQuery] 修饰
public object Method1(int id, [FromQuery]string keyword)
{
}

5.8.17 [FormRoute] 路由约束

Furion v2.8.6 版本中,添加了 [RouteConstraint] 特性,可配置路由约束,如:[RouteConstraint(":min(10)")]

// 最小值 10
public object Method1([RouteConstraint(":min(10)")] int id)
{
}

[RouteConstraint] 支持路由约束符号如下:

符号描述例子
alpha匹配大写或小写拉丁字母字符(a-z、A-Z):alpha
boolbool 类型:bool
datetimeDateTime 类型:datetime
decimaldecimal 类型:decimal
doubledouble 类型:double
floatfloat 类型:float
guidguid 类型:guid
intint 类型:int
longlong 类型:long
length匹配长度(字符串):length(6):length(1,20)
max最大值:max(10)
maxlength最大长度(字符串):maxlength(10)
min最小值:min(10)
minlength最小长度(字符串):minlength(10)
range取值范围:range(10,50)
regex正则表达式:regex(^\d{3}-\d{3}-\d{4}$)

5.8.18 小驼峰 路由路径

{
"DynamicApiControllerSettings": {
"LowercaseRoute": false,
"KeepName": true,
"AsLowerCamelCase": true
}
}

5.9 [ApiDescriptionSettings]

除了上述 ASP.NET Core 提供的配置外,Furion 框架还提供了非常强大且灵活的 [ApiDescriptionSettings] 特性。

5.9.1 内置配置

  • Name:自定义控制器/动作方法名称,string,默认 null
  • KeepName:是否保持原有名称不处理,bool,默认 false
  • SplitCamelCase:切割骆驼(驼峰)/帕斯卡命名,bool,默认 true
  • KeepVerb:是否保留动作方法请求谓词,bool,默认 false
  • Enabled:是否导出接口,bool,默认 true
  • Module:模块名,string,默认 null
  • Version:版本号,string,默认 null
  • Groups:接口分组,可结合 Swagger 一起使用,string[],默认 null
  • Tags:接口标签,可结合 Swagger 一起使用,string[],默认 null
  • Order:配置控制器/动作方法排序
  • LowercaseRoute:是否采用小写路由,bool 类型,默认 true
  • AsLowerCamelCase:启用小驼峰命名(首字母小写),默认 false
  • Area:配置区域名称,默认空,只作用于类中贴

5.9.2 Name 配置

Name 参数可以覆盖动态 WebAPI 自动生成的控制器或动作方法名称。如:

using Furion.DynamicApiController;

namespace Furion.Application
{
[ApiDescriptionSettings(Name = "MyFur")]
public class FurionAppService : IDynamicApiController
{
[ApiDescriptionSettings(Name = "MyGet")]
public string Get()
{
return nameof(Furion);
}
}
}

如下图所示:

5.9.3 KeepName 配置

KeepName 参数可以保留原有的控制器或动作方法名称。如:

using Furion.DynamicApiController;

namespace Furion.Application
{
[ApiDescriptionSettings(KeepName = true)]
public class FurionAppService : IDynamicApiController
{
[ApiDescriptionSettings(KeepName = true)]
public string Get()
{
return nameof(Furion);
}
}
}

如下图所示:

5.9.4 SplitCamelCase 配置

SplitCamelCase 参数默认将骆驼(驼峰)命名切割成多个单词并通过指定 占位符 连接起来。默认 占位符-。默认为 true。如:

using Furion.DynamicApiController;

namespace Furion.Application
{
[ApiDescriptionSettings(SplitCamelCase = false)]
public class MyFurionAppService : IDynamicApiController
{
[ApiDescriptionSettings(SplitCamelCase = true)]
public string ChangeUserName()
{
return nameof(Furion);
}
}
}

如下图所示:

特别注意

KeepName 优先级高于 SplitCamelCase,也就是 KeepName 设置为 true,则不会处理 SplitCamelCase 参数。

5.9.5 KeepVerb 配置

KeepVerb 参数作用于动作方法,标识是否保留动作谓词。如:

using Furion.DynamicApiController;

namespace Furion.Application
{
public class FurionAppService : IDynamicApiController
{
[ApiDescriptionSettings(KeepVerb = true)]
public string GetVersion()
{
return nameof(Furion);
}
}
}

如下图所示:

5.9.6 Enabled 配置

Enabled 参数配置接口是否导出。通常用于动作方法,如果用于控制器实际作用不大。

using Furion.DynamicApiController;

namespace Furion.Application
{
public class FurionAppService : IDynamicApiController
{
public string GetVersion()
{
return nameof(Furion);
}

[ApiDescriptionSettings(false)]
public string NoExport()
{
return nameof(Furion);
}
}
}

如下图所示:

5.9.7 Module 配置

Module 参数可以配置路由分离,类似于 Mvc 区域 的作用。

using Furion.DynamicApiController;

namespace Furion.Application
{
[ApiDescriptionSettings(Module = "mobile")]
public class FurionAppService : IDynamicApiController
{
[ApiDescriptionSettings(Module = "user")]
public string GetVersion()
{
return nameof(Furion);
}
}
}

如下图所示:

5.9.8 Version 配置

Version 参数可以配置接口版本,同时又可以复写特殊版本命名配置。默认版本分隔符为 @。如:

using Furion.DynamicApiController;

namespace Furion.Application
{
[ApiDescriptionSettings(Version = "1.0")]
public class FurionAppService : IDynamicApiController
{
// V2.0.0 被复写成 V2.1.1
[ApiDescriptionSettings(Version = "2.1.1")]
public string GetVersionV2_0_0()
{
return nameof(Furion);
}
}
}

如下图所示:

5.9.9 Groups 配置

Groups 配置主要用于配置 Swagger 分组信息。

通过配置 Groups 参数可以将控制器和动作方法 进行归类和多个分组直接共享。可通过 [ApiDescriptionSettings(params Groups)] 构造函数传入或指定 Groups 参数配置接口是否导出。通常用于动作方法,如果用于控制器实际作用不大。

using Furion.DynamicApiController;

namespace Furion.Application
{
[ApiDescriptionSettings("Default", "Common")]
public class FurionAppService : IDynamicApiController
{
public string Get()
{
return nameof(Furion);
}

[ApiDescriptionSettings("Custom")]
public int Get(int id)
{
return id;
}
}
}

如下图所示:

5.9.10 Tag 配置

Tag 配置主要用于配置 Swagger 标签分组信息及合并标签。也就是 组中组:

未贴标签之前

using Furion.DynamicApiController;

namespace Furion.Application
{
public class FurionAppService : IDynamicApiController
{
public string Get()
{
return nameof(Furion);
}

public int Get(int id)
{
return id;
}
}

public class TestAppService : IDynamicApiController
{
public string Get()
{
return nameof(Furion);
}

public int Get(int id)
{
return id;
}
}
}

贴标签之后

using Furion.DynamicApiController;

namespace Furion.Application
{
[ApiDescriptionSettings(Tag = "分组一")]
public class FurionAppService : IDynamicApiController
{
public string Get()
{
return nameof(Furion);
}

public int Get(int id)
{
return id;
}
}

[ApiDescriptionSettings(Tag = "分组二")]
public class TestAppService : IDynamicApiController
{
public string Get()
{
return nameof(Furion);
}

public int Get(int id)
{
return id;
}
}
}

如下图所示:

小知识

如果 Tag 名字一样,则会自动合并,否则只是命名。

5.10 DynamicApiControllerSettings 配置

Furion 还提供动态 WebAPI 接口一些全局配置选项,如:

  • DefaultRoutePrefix:默认路由前缀,string,默认 api
  • DefaultHttpMethod:默认请求谓词,string,默认:POST
  • DefaultModule:默认模块名称(区域),可用作接口版本,string,默认:v1
  • LowercaseRoute:小写路由格式,bool,默认:true
  • AsLowerCamelCase:启用小驼峰命名(首字母小写),默认 false
  • KeepVerb:是否保留动作谓词,bool,默认:false
  • KeepName:是否保留默认名称,bool,默认:fasle
  • CamelCaseSeparator:骆驼(驼峰)/帕斯卡命名分隔符,string,默认:-
  • VersionSeparator:版本分隔符,string,默认:@
  • ModelToQueryGET/HEAD 请求将 类类型参数转查询参数bool,默认 false
  • SupportedMvcController:是否支持 Mvc Controller 动态配置,bool,默认 false
  • UrlParameterization:路由参数采用 [FromQuery] 化,默认 false[FromRoute] 方式)
  • DefaultArea:配置默认区域,默认 null
  • AbandonControllerAffixes:默认去除控制器名称前后缀列表名,string[],默认:
    • AppServices
    • AppService
    • ApiController
    • Controller
    • Services
    • Service
  • AbandonActionAffixes:默认去除动作方法名称前后缀列表名,string[],默认:
    • Async
    • VerbToHttpMethods:复写默认方法名转 [HttpMethod] 规则,string[][] 二维数组类型,内置匹配规则为:
      ["post"] = "POST",
      ["add"] = "POST",
      ["create"] = "POST",
      ["insert"] = "POST",
      ["submit"] = "POST",
      ["get"] = "GET",
      ["find"] = "GET",
      ["fetch"] = "GET",
      ["query"] = "GET",
      ["getlist"] = "GET",
      ["getall"] = "GET",
      ["put"] = "PUT",
      ["update"] = "PUT",
      ["delete"] = "DELETE",
      ["remove"] = "DELETE",
      ["clear"] = "DELETE",
      ["patch"] = "PATCH"
      • 复写示例
      "DynamicApiControllerSettings": {
      "VerbToHttpMethods": [
      [ "getall", "HEAD" ], // => getall 会被复写为 `[HttpHead]`
      [ "other", "PUT" ] // => 新增一条新规则,比如,一 `[other]` 开头会转换为 `[HttpPut]` 请求
      ]
      }

5.10.1 支持 Mvc 控制器 动态配置

默认情况下,Furion 动态 WebAPI 接口不对 ControllerBase 类型进行任何处理。当然,我们也可以手动启用 ControllerBase 支持。

Furion.Web.Entry/appsettings.json
{
"DynamicApiControllerSettings": {
"SupportedMvcController": true
}
}

设置 SupportedMvcController: true 后,Mvc ControllerBase 类型也能和动态 WebAPI 一样的灵活了。代码如下:

using Microsoft.AspNetCore.Mvc;

namespace Furion.Web.Entry.Controllers
{
public class MvcController : ControllerBase
{
public string Get()
{
return nameof(Furion);
}
}
}

注意事项

启用该配置后,如果 Mvc 控制器 没有任何 [Route] 特性,但是贴了 [ApiController] 特性将会报错。原因是 [ApiController] 特性内部做了路由特性检测。所以建议使用 [ApiDataValidation] 代替。

查看 ASP.NET Core - ApiBehaviorApplicationModelProvider 源码

5.11 关于 AOP 拦截

动态WebAPI 支持 Controller 的所有过滤器/筛选器拦截,也就是可以通过 ActionFilterResultFilter 进行拦截操作。如:

public class SampleAsyncActionFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context,ActionExecutionDelegate next)
{
// 拦截之前

var resultContext = await next();

// 拦截之后

// 异常拦截
if(resultContext.Exception != null)
{

}
}
}

详细用法可参见 ASP.NET Core 5.0 - 筛选器

5.12 设置 api 超时请求时间

Program.cs 中添加 .UseKestrel 配置即可,如:

  • .NET5 版本
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.Inject()
.UseStartup<Startup>()
.UseKestrel(option =>
{
option.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(20);
option.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(20);
});
});
}
  • .NET6 版本
var app = builder.Build();

app.Configuration.Get<WebHostBuilder>().ConfigureKestrel(x =>
{
x.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(20);
x.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(20);
});

5.13 反馈与建议

与我们交流

给 Furion 提 Issue