5.1 动态 WebAPI
📝 模块更新日志
- 新特性
-
[FlexibleArray<T>]特性支持在属性上标注 4.9.8.16 ⏱️2026.02.11 IDQJ4E 1302055 - 动态
WebAPI支持自定义生成规则 4.9.6.13 ⏱️2024.12.09 33c78d6 - 动态
WebAPI可配置某个Action不被派生类继承后生成路由的特性[ApiDescriptionSettings(DisableInherite = true)]4.9.5.8 ⏱️2024.09.10 7f248cf -
[FlexibleArray]模型绑定特性,解决URL地址传递数组类型参数问题 4.9.3.5 ⏱️2024.05.20 e891f0e - 动态
WebAPI支持配置基元类型和字符串类型默认绑定信息 4.9.2.32 ⏱️2024.04.28 d7e7a02 - 动态
WebAPI支持贴[Route]特性动态生成控制器 4.9.2.19 ⏱️2024.04.16 #I9H1QH - 动态
WebAPI支持text/plain格式的Body参数 4.8.8.9 ⏱️2023.05.04 b49fe50 - 插件化
IDynamicApiRuntimeChangeProvider接口,可在运行时动态添加WebAPI/Controller4.8.8.8 ⏱️2023.05.04 322ea59
-
查看变化
在一些特定的需求中,我们需要在运行时动态编译代码,如动态编写 WebAPI,之后能够在不重启主机服务的情况下即可有效。比如这里动态添加 SomeClass 动态 WebAPI,然后在 Swagger/路由系统 中立即有效:
using Furion;
using Furion.DynamicApiController;
using Microsoft.AspNetCore.Mvc;
namespace YourProject.Application;
public class PluginApiServices : IDynamicApiController
{
private readonly IDynamicApiRuntimeChangeProvider _provider;
public PluginApiServices(IDynamicApiRuntimeChangeProvider provider)
{
_provider = provider;
}
/// <summary>
/// 动态添加 WebAPI/Controller
/// </summary>
/// <param name="csharpCode"></param>
/// <param name="assemblyName">可自行指定程序集名称</param>
/// <returns></returns>
public string Compile([FromBody] string csharpCode, [FromQuery] string assemblyName = default)
{
// 编译 C# 代码并返回动态程序集
var dynamicAssembly = App.CompileCSharpClassCode(csharpCode, assemblyName);
// 将程序集添加进动态 WebAPI 应用部件
_provider.AddAssembliesWithNotifyChanges(dynamicAssembly);
// 返回动态程序集名称
return dynamicAssembly.GetName().Name;
}
/// <summary>
/// 移除动态程序集 WebAPI/Controller
/// </summary>
public void Remove(string assemblyName)
{
_provider.RemoveAssembliesWithNotifyChanges(assemblyName);
}
}
这时只需要请求 api/plugin-api/compile 接口同时设置请求 Content-Type 为 text/plain,接下来传入 C# 代码字符串 即可,如:
using Furion.DynamicApiController;
namespace YourProject.Application;
public class SomeClass : IDynamicApiController
{
public string GetName()
{
return nameof(Furion);
}
}

之后刷新浏览器即可看到最新的 API:

还可以在运行时动态卸载,使用 DELETE 请求 api/plugin-api 即可。
-
- 动态
WebAPI自动检查路由是否包含重复参数,如果有自动修正而不是抛异常 4.8.6.5 ⏱️2023.02.17 5f15ea1
- 动态
查看变化
在 Furion 4.8.6.5 之前,下列代码会抛出异常:The route parameter name 'roleid' appears more than one time in the route template.
原因是生成的路由包含了多个 {roleId}:/api/with-class/system/role/deptTree/{roleId}/{roleId}。
public class WithClass : IDynamicApiController
{
[HttpGet("system/role/deptTree/{roleId}")] // 过去版本抛异常,Furion 4.8.6.5+ 正常~
public string GetResult2(string roleId)
{
return nameof(Furion);
}
}
新版本 Furion 4.8.6.5+ 修正了该错误,自动移除后面重复的路由参数且不再抛异常,也就是最终生成路由为:/api/with-class/system/role/deptTree/{roleId}
-
- 动态
WebAPI支持[RouteConstraint(":*")]路由约束 4.8.6.2 ⏱️2023.02.10 #I6E6JA
- 动态
-
- 动态
WebAPI支持更加强大的路由组合功能 4.8.5.7 ⏱️2023.02.03 #I6CLPT
- 动态
查看变化
using Furion.DynamicApiController;
using Microsoft.AspNetCore.Mvc;
namespace WebApplication38;
[Route("api/[controller]")]
[Route("api2/[controller]")]
public class Test1Service : IDynamicApiController
{
[HttpGet("test")]
[HttpPost]
[AcceptVerbs("PUT", "PATCH")]
public async Task GetTestName()
{
await Task.CompletedTask;
}
}
public class Test2Service : IDynamicApiController
{
[HttpGet("/root/test")]
[HttpGet("test")]
[HttpGet(Name = "other-test")]
[HttpGet("template-test", Name = "other-test")]
[HttpPost]
[AcceptVerbs("PUT", "PATCH")]
public async Task GetTestName()
{
await Task.CompletedTask;
}
}
[Route("api/[controller]")]
[Route("api2/[controller]/second")]
[Route("api3/[controller]/three")]
public class Test3Service : IDynamicApiController
{
[HttpGet]
[HttpGet("get/[action]")]
[HttpPost]
[HttpPost("post/cus-version")]
public string GetVersion()
{
return "1.0.0";
}
}

- 突破性变化
查看变化
在过去,TestMethod 生成路由为:/mytest
// 注意这里没有 [Route] 特性
public class ClassService: IDynamicApiController
{
[HttpPost("mytest")]
public void TestMethod()
{
}
}
新版本:TestMethod 生成路由为:/api/class/mytest,TestMethod2 生成路由为:/mytest。
// 注意这里没有 [Route] 特性
public class ClassService: IDynamicApiController
{
[HttpPost("mytest")]
public void TestMethod()
{
}
[HttpPost("/mytest")]
public void TestMethod2()
{
}
}
也就是新版本如果不需要自动添加前缀,需在前面添加 /,旧版本不需要。
- 问题修复
- 因
v4.9.7.207版本导致WebAPI无法返回application/xml格式 4.9.7.245 ⏱️2026.01.12 #IDJ3MZ - 运行时动态生成的控制器未应用自定义过滤方法的问题 4.9.7.134 ⏱️2025.10.25 c21f9dc
- 动态
WebAPI设置UrlParameterization: true配置后显式标注参数[FromRoute]特性无效问题 4.9.7.61 ⏱️2025.05.12 #IC6WNL - 动态
WebAPI不支持[NonController]特性标注处理问题 4.9.6.15 ⏱️2024.12.10 d3fdd24 - 动态添加程序集插件后无法扫描控制器和动态
WebAPI4.9.5.15 ⏱️2024.10.11 #IAWDBM - 动态
WebAPI不支持[BindNever]特性忽略路由和Action参数设置 4.9.2.25 ⏱️2024.04.19 21599e6 - 动态
WebAPI错误将CancellationToken类型当作路由参数 4.9.2.19 ⏱️2024.04.16 #I9H14X - 动态
WebAPI自定义[Route]模板中包含路由约束并且含有大小写字母导致生成错误路由问题 4.9.1.61 ⏱️2024.03.27 cc1a7ec - 动态
WebAPI配置了DefaultModule后导致生成重复的模块路由,由 87849d1 提交导致 4.9.1.49 ⏱️2024.03.14 #I98GDW - 动态
WebAPI设置VersionInFront: false异常问题 4.9.1.26 ⏱️2024.01.12 #I8ZGGH #I8ZDXD #I8ZFNG - 动态
WebAPI配置ForceWithRoutePrefix不包含Module问题 4.9.1.19 ⏱️2024.01.09 87849d1 - 动态
WebAPI因 1dc7ea7 提交导致AsLowerCamelCase配置失效问题 4.9.1.15 ⏱️2023.12.20 d9810be - 动态
WebAPI不能正确移除AppService命名的Service问题 4.8.8.47 ⏱️2023.10.10 #I86NL - 动态
WebAPI自定义路由模板参数和自动拼接参数冲突问题 4.8.8.15 ⏱️2023.05.15 #I72ZZ2 - 动态
WebAPI去除叠词类型命名如ServiceService前后缀异常问题 4.8.7.32 ⏱️2023.04.02 #I6SB3Z - 动态
WebAPI不支持嵌套继承[Route]特性问题 4.8.6.8 ⏱️2023.02.18 #I6CLPT
- 因
查看变化
过去版本生成错误重复路由,如:api/system/SystemDictionary/api/system/SystemDictionary/Add,现已修正。
public class WithClass : IDynamicApiController
{
[Route("Add")]
public void Add()
{
}
[Route("Edit")]
public void Edit()
{
}
}
[Route("api/system/SystemDictionary")]
public class SystemService : WithClass
{
public void Some()
{
}
}
动态WebAPI 实际上就是将普通的类变为 Controller,也就是 动态WebAPI 就是控制器,支持控制器一切功能。
5.1.1 什么是控制器
简单来说,控制器是一个承上启下的作用,根据用户输入,执行响应行为(动作方法),同时在行为中调用模型的业务逻辑,返回给用户结果(视图)。
在 ASP.NET Core 中,控制器有两种表现形式:
Mvc(带视图)WebAPI(RESTful API)
- Mvc 控制器
- WebAPI 控制器
using Microsoft.AspNetCore.Mvc;
namespace Furion.Web.Entry.Controllers
{
public class MvcController : Controller
{
public IActionResult Index()
{
return View();
}
}
}
using Microsoft.AspNetCore.Mvc;
namespace Furion.Web.Entry.Controllers
{
[Route("api/[controller]")]
public class WebApiController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Content(nameof(Furion));
}
}
}
Mvc 控制器和 WebAPI 控制器最大的区别是 WebAPI 控制器不带 视图 和通过 请求谓词和路由地址响应行为。
5.1.2 Mvc 控制器 约定和缺点
在学习动态 WebAPI 控制器之前,首先了解 ASP.NET Core 中 WebAPI 的一些约定和注意事项。
5.1.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.1.2.2 .NET Core WebAPI 缺点
通过上一章节可以看出,ASP.NET Core 应用实现 WebAPI 需要遵循种种约定,而且容易出错。
除了这些约定,.NET Core WebAPI 有以下缺点:
- 路由地址基本靠手工完成
- 在现在移动为王的时代,不利于进行多版本控制
- 对接
Swagger文档分组比较复杂 - 实现
Policy策略授权也比较复杂 - 不支持控制器热插拔插件化
- 难以实现复杂自定义的
RESTful API风格
5.1.3 动态 WebAPI 控制器
针对以上 ASP.NET Core 提供的 WebAPI 必须遵循的约定和不可避免的缺点,Furion 框架推出一种更加灵活创建 WebAPI 控制器的方式。
这个方式在继承了 ASP.NET Core WebAPI 所有优点,同时进行了大量扩展和优化。优化后的 WebAPI 具有以下优点:
- 具备原有的
ControllerBase所有功能 - 支持任意公开 非静态 非抽象 非泛型类转控制器
- 提供更加灵活方便的
IDynamicApiController空接口或[DynamicApiController]特性替代ControllerBase抽象类 - 可直接在任意公开 非静态 非抽象 非泛型类贴
[Route]特性自动转控制器 - 无需手动配置
[HttpMethod]特性,同时支持一个动作方法多个HttpVerb - 无需手动配置
[Route]特性,支持更加灵活的配置及自动路由生成 - 支持返回泛型接口,泛型类
- 和
Swagger深度结合,提供极其方便的创建Swagger分组配置 - 支持
Basic Auth,Jwt,ApiKey等多种权限灵活配置 - 支持控制器、动作方法版本控制功能
- 支持
GET、HEAD请求自动转换类类型参数 - 支持生成
OAS3接口规范
5.1.4 注册动态 WebAPI 服务
.AddDynamicApiControllers() 默认已经集成在 AddInject() 中了,无需再次注册。也就是下列代码可不配置。
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.1.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)}";
}
}
}
[Route("路由模板")]方式
以下内容仅限 Furion 4.9.2.19 + 版本使用。
// [ApiController] // 此特性可选,主要实现模型绑定验证
[Route("[controller]")] // 仅需添加此路由特性,即可实现控制器的生成
public class RouteController
{
public void Get()
{
}
}
如下图所示,一个 WebAPI 接口就这么生成了。
5.1.6 动态 WebAPI 原理解析
5.1.6.1 控制器特性提供器
Furion 框架会在应用启动时注册 DynamicApiControllerFeatureProvider 控制器特性提供器,该提供器继承自 ControllerFeatureProvider 类。
接着重写 bool IsController(TypeInfo typeInfo) 方法,用来标识控制器类型。在 Furion 框架中,继承自 ControllerBase 类或 IDynamicApiController 接口或 [DynamicApiController] 特性都会被标记为控制器类型。
5.1.6.2 应用模型转换器
Furion 框架同时在应用启动时注册 DynamicApiControllerApplicationModelConvention 应用模型转换器,该转换器继承自 IApplicationModelConvention 接口。
接着实现 void Apply(ApplicationModel application) 接口方法。在该方法中配置控制器名称、路由、导出可见性及动作方法名称、路由、导出可见性等。
实际上该方法做的就是按照 WebAPI 约定 提前帮我们配置好路由、请求谓词等信息。避免了手动配置的同时还增加了许多新特性,如版本控制。
5.1.7 动态 WebAPI 配置约定
5.1.7.1 控制器默认约定
- 生成控制器名称默认去除以
AppServices,AppService,ApiController,Controller,Services,Service作为前后缀的字符串。见第一个例子中的FurionAppService -> Furion支持自定义配置 - 控制器名称带
V[0-9_]结尾的,会自动生成控制器版本号,如FurionAppServiceV2 -> Furion@2,FurionAppServiceV1_1_0 -> Furion@1.1.0。支持版本分隔符配置 - 控制名称以
骆驼命名(CamelCase)会自动切割成多个单词-连接。支持自定义配置
5.1.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@2,ChangePasswordV1_1_0 -> ChangePassword@1.1.0。支持版本分隔符配置 - 动作方法名称以
骆驼(驼峰)/帕斯卡命名(CamelCase/Pascal)会自动切割成多个单词-连接。支持自定义配置 - 动作方法参数将自动转为小写。支持自定义配置
5.1.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.1.7.4 路由地址默认约定
- 默认以
api开头。支持自定义配置 - 默认转换为小写路由地址。支持自定义配置
- 生成控制器路由模板格式为:
api/前置参数列表/模块名或默认区域名/[controller@版本号]/后置参数列表 - 生成动作方法路由模板格式为:
前置参数列表/模块名/[action@版本号]/后置参数列表
5.1.7.5 其他约定
- 默认不处理
ControllerBase控制器类型。支持自定义配置 - 默认不处理
GET,HEAD请求的引用类型参数。支持自定义配置
5.1.8 更多例子
5.1.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.1.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.1.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.1.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 框架支持以下两种方式配置:
- [FromQuery] 特性
- 配置 DynamicApiControllerSettings
using Furion.DynamicApiController;
using Microsoft.AspNetCore.Mvc;
namespace Furion.Application
{
public class FurionAppService : IDynamicApiController
{
public TestDto GetTest([FromQuery] TestDto testDto)
{
return testDto;
}
}
}
{
"DynamicApiControllerSettings": {
"ModelToQuery": true
}
}
如下图所示:
5.1.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.1.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.1.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.1.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";
}
}
}
如下图所示:

using Furion.DynamicApiController;
using Microsoft.AspNetCore.Mvc;
namespace Furion.Application
{
public class FurionAppService : IDynamicApiController
{
[Route("customapi/[action]")]
public string GetVersion()
{
return "1.0.0";
}
}
}
如下图所示:

using Furion.DynamicApiController;
using Microsoft.AspNetCore.Mvc;
namespace Furion.Application
{
[Route("customapi/mobile/[controller]")]
public class FurionAppService : IDynamicApiController
{
[Route("get/[action]")]
public string GetVersion()
{
return "1.0.0";
}
}
}
如下图所示:

using Furion.DynamicApiController;
using Microsoft.AspNetCore.Mvc;
namespace Furion.Application
{
[Route("api/[controller]")]
public class FurionAppService : IDynamicApiController
{
[HttpGet("get/[action]")]
public string GetVersion()
{
return "1.0.0";
}
}
}
如下图所示:

动作方法自定义路由如果以 / 开头,则不会合并控制器路由。
自定义路由如果需要用到 控制器/动作方法名称,推荐使用 [controller] 或 [action] 占位符,因为该占位符已经自动处理了 前后缀、版本号、模块名称等。
5.1.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] 特性,只能二取一。
在 Furion 4.8.5.7+ 版本提供更加强大的路由组合方式:
using Furion.DynamicApiController;
using Microsoft.AspNetCore.Mvc;
namespace WebApplication38;
[Route("api/[controller]")]
[Route("api2/[controller]")]
public class Test1Service : IDynamicApiController
{
[HttpGet("test")]
[HttpPost]
[AcceptVerbs("PUT", "PATCH")]
public async Task GetTestName()
{
await Task.CompletedTask;
}
}
public class Test2Service : IDynamicApiController
{
[HttpGet("/root/test")]
[HttpGet("test")]
[HttpGet(Name = "other-test")]
[HttpGet("template-test", Name = "other-test")]
[HttpPost]
[AcceptVerbs("PUT", "PATCH")]
public async Task GetTestName()
{
await Task.CompletedTask;
}
}
[Route("api/[controller]")]
[Route("api2/[controller]/second")]
[Route("api3/[controller]/three")]
public class Test3Service : IDynamicApiController
{
[HttpGet]
[HttpGet("get/[action]")]
[HttpPost]
[HttpPost("post/cus-version")]
public string GetVersion()
{
return "1.0.0";
}
}
5.1.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);
}
}
}
如下图所示:

using Furion.DynamicApiController;
namespace Furion.Application
{
public class FurionAppService : IDynamicApiController
{
public string Get()
{
return nameof(Furion);
}
public string GetV1()
{
return nameof(Furion);
}
public string GetV2_1()
{
return nameof(Furion);
}
}
}
如下图所示:

V[0-9_] 结尾的命名自动解析成版本号,如 FurionAppServiceV2 -> v2/Furion。
除了通过特定后缀方式以外,版本还直接通过 [ApiDescriptionSettings] 进行复写。如:
[ApiDescriptionSettings(Version = "4.0")]
public string GetV1()
{
return nameof(Furion);
}
这时,生成版本将采用 4.0 替代 1
5.1.8.11 不公开控制器或动作方法
有些时候,我们无需导出某个动作方法或控制器(不显示到 Swagger),只需要添加 [NonController]、 [ApiDescriptionSettings(false)] 或 [ApiDescriptionSettings(IgnoreApi = true)]即可。
另外动作方法还支持 [NonAction] 标记不是一个有效的控制器或 Action。
public class FurionAppService: IDynamicApiController
{
public string Export()
{
// ....
}
[ApiDescriptionSettings(false)] // 不在 Swagger 上显示
public string NoExport()
{
// ...
}
[ApiDescriptionSettings(IgnoreApi = true)] // 不在 Swagger 上显示
public string NoExport2()
{
// ...
}
[NonAction] // 不是一个 API
public string IsNotAPI()
{
// ...
}
}
[ApiDescriptionSettings(false)] // 不导出
public class NoExportServices: IDynamicApiController
{
// ....
}
[NonController] // 标记不是一个控制器(支持控制器和动态 WebAPI)
public class TestController: ControllerBase
{
// ....
}
5.1.8.12 保持控制器和方法命名
默认情况下,动态 API 会将控制器和方法名输出为 RESTFul 风格的路由,如需保留原有设计,只需配置:
{
"DynamicApiControllerSettings": {
"KeepName": true,
"KeepVerb": true,
"LowercaseRoute": false
}
}
5.1.8.13 方法参数 [FromQuery] 化/参数非必填/参数可选
默认情况下,所有的基元类型参数都会贴上 [FromRoute] 特性(Furion 4.9.2.32+ 版本支持修改默认绑定方式。),如果需要将参数调整为 [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.1.8.14 参数绑定配置
Furion 框架提供了多种参数特性配置参数绑定规则:
[FromRoute]:通过路由参数绑定值[FromQuery]:通过Url地址参数绑定值[FromBody]:通过Request Body参数绑定值[FromForm]:通过表单提交绑定值[FromHeader]:通过Request Header参数绑定值
5.1.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.1.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.1.8.17 [FormRoute] 路由约束
在 Furion v2.8.6 版本中,添加了 [RouteConstraint] 特性,可配置路由约束,如:[RouteConstraint(":min(10)")]
// 最小值 10
public object Method1([RouteConstraint(":min(10)")] int id)
{
}
[RouteConstraint] 支持路由约束符号如下:
| 符号 | 描述 | 例子 |
|---|---|---|
* | 匹配路由 0-n 长度,Furion 4.8.6.2+ 支持 | :* |
alpha | 匹配大写或小写拉丁字母字符(a-z、A-Z) | :alpha |
bool | bool 类型 | :bool |
datetime | DateTime 类型 | :datetime |
decimal | decimal 类型 | :decimal |
double | double 类型 | :double |
float | float 类型 | :float |
guid | guid 类型 | :guid |
int | int 类型 | :int |
long | long 类型 | :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.1.8.18 小驼峰 路由路径
{
"DynamicApiControllerSettings": {
"LowercaseRoute": false,
"KeepName": true,
"AsLowerCamelCase": true
}
}
5.1.8.19 application/xml 报文参数支持
- 在
Startup.cs中启用XML请求报文格式支持
services.AddControllers() // .AddControllersWithViews()
.AddXmlSerializerFormatters()
.AddXmlDataContractSerializerFormatters()
如果出现 XmlSerializer 异常,那么只需要移除 .AddXmlSerializerFormatters() 即可,如:
services.AddControllers() // .AddControllersWithViews()
.AddXmlDataContractSerializerFormatters()
- 定义实体类型
// 实体类型
public class People
{
// 基础类型
public int Age { get; set; }
public string Name { get; set; }
public bool IsDeleted { get; set; }
// 数组类型
public string[] Address { get; set; }
// 集合类型
public List<string> Emails { get; set; }
// 类类型
public Child Child { get; set; }
}
public class Child
{
public string Name { get; set; }
}
- 在动态
WebAPI中使用
public class XmlDemo : IDynamicApiController
{
//[Consumes("application/xml")] // 如果设置了 [Consumes] 那么就表示只能传递 `accept= application/xml` 格式,不设置则支持多种(XML/JSON)
public People Test(People people)
{
return people;
}
}
[Consumes] 特性是用来定义输入参数格式,对应请求报文的 accept,[Produces] 特性是用来定义返回值格式,对应响应报文的 content-type。
XML格式说明及注意事项
支持两种 XML 格式报文,如:
XML 标签区分大小写,必须严格对照 C# 类型定义声明。对于集合/数组类型,必须遵循下列格式:
<属性名>
<C#类型></C#类型>
</属性名>
如:
<Strings>
<string>monksoul@outlook.com</string>
<string>rustln@outlook.com</string>
</Strings>
<Ints>
<int>1</int>
<int>2</int>
</Ints>
<Bools>
<boolean>true</boolean>
<boolean>false</boolean>
</Bools>
<Decimals>
<decimal>1.0</decimal>
<decimal>2.0</decimal>
</Decimals>
<Floats>
<float>1.0</float>
<float>2.0</float>
</Floats>
<Childs>
<Child>
<Name>Furion</Name>
</Child>
</Childs>
- 第一种(常用格式)
<?xml version="1.0" encoding="UTF-8"?>
<People>
<Age>30</Age>
<Name>百小僧</Name>
<IsDeleted>true</IsDeleted>
<Address>
<string>广东省中山市</string>
<string>广东省珠海市</string>
</Address>
<Emails>
<string>monksoul@outlook.com</string>
<string>rustln@outlook.com</string>
</Emails>
<Child>
<Name>Furion</Name>
</Child>
</People>
- 第二种(标准格式)
<People xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Age>30</Age>
<Name>百小僧</Name>
<IsDeleted>true</IsDeleted>
<Address>
<string>广东省中山市</string>
<string>广东省珠海市</string>
</Address>
<Emails>
<string>monksoul@outlook.com</string>
<string>rustln@outlook.com</string>
</Emails>
<Child>
<Name>Furion</Name>
</Child>
</People>
5.1.8.20 监听请求取消 CancellationToken 参数
以下内容仅限 Furion 4.9.2.19 + 版本使用。
在实际应用中,经常需要监控客户端发送的请求是否在中途意外中断,例如服务器尚未完成响应时,用户关闭了浏览器窗口或标签页,或其他不可预见的因素导致请求未能完整执行。
针对这种情况,我们可以在 Action 方法的参数列表中增加 CancellationToken 参数。此参数的作用在于能够监听客户端是否提前终止了请求,从而进行相应的处理。
[HttpGet]
public async Task SomeApi(CancellationToken cancellationToken)
{
// 监听客户端是否终止请求
cancellationToken.Register(() =>
{
Console.WriteLine("用户中断了请求。");
});
// 确保在异步方法中使用 cancellationToken
await Task.Run(() => {
// 模拟异步操作
}, cancellationToken);
}
注意,在异步方法中,应该确保 CancellationToken 被正确传递并用于可能中断的异步操作。在上面的代码中,我们使用了 Task.Run 来模拟一个异步操作,并将 cancellationToken 作为参数传递,以便在需要时能够中断该操作。
5.1.8.21 忽略参数绑定(生成路由)
以下内容仅限 Furion 4.9.2.25 + 版本使用。
在一些特殊的场景,我们可能需要忽略某个参数进行路由生成或者模型绑定,这时候可使用 [BindNever] 特性,如:
public void TestBindNever([BindNever] string never, int id) // never 贴有 [BindNever] 特性
{
}
如果需要忽略某个类型的属性,可通过以下方式:
[Newtonsoft.Json.JsonIgnore] // 针对 Newtonsoft
[System.Text.Json.Serialization.JsonIgnore] // 针对 System.Text.Json
public string PropertyName {get; set;}
5.1.8.22 绑定数组类型 URL 参数
以下内容仅限 Furion 4.9.3.5 + 版本使用。
默认情况下,ASP.NET Core 绑定数组类型的 URL 参数时,只支持 name=value&name=value 形式设置,但不支持以下两种方式:
http://localhost:5000/get-array?status[]=a&status[]=b(使用[]后缀)http://localhost:5000/get-array?status=a,b(使用逗号分隔值)
这时,可通过在数组类型参数贴 [FlexibleArray<T>] 特性即可,如:
public void GetArray([FromQuery]
[FlexibleArray<string>]List<string> status)
{
}
其中 FlexibleArray 的泛型类型 T 为数组(集合)元素的类型。支持在属性和参数上标注。
5.1.9 [ApiDescriptionSettings]
除了上述 ASP.NET Core 提供的配置外,Furion 框架还提供了非常强大且灵活的 [ApiDescriptionSettings] 特性。
5.1.9.1 内置配置
Name:自定义控制器/动作方法名称,string,默认nullKeepName:是否保持原有名称不处理,bool,默认falseSplitCamelCase:切割骆驼(驼峰)/帕斯卡命名,bool,默认trueKeepVerb:是否保留动作方法请求谓词,bool,默认falseEnabled:是否导出接口,bool,默认trueModule:模块名,string,默认nullVersion:版本号,string,默认nullGroups:接口分组,可结合Swagger一起使用,string[],默认nullTags:接口标签,可结合Swagger一起使用,string[],默认nullOrder:配置控制器/动作方法排序,数值越大越靠前LowercaseRoute:是否采用小写路由,bool类型,默认trueAsLowerCamelCase:启用小驼峰命名(首字母小写),默认falseArea:配置区域名称,默认空,只作用于类中贴Description:配置单一接口更多描述功能,只在方法中有效,仅限 v3.3.5+版本有效ForceWithRoutePrefix:配置是否强制添加DefaultRoutePrefix,当控制器自定义了[Route]有效,默认false,仅限 v3.4.1+版本有效DisableInherite:配置某个Action是否被派生类继承时生成路由,默认false,Furion 4.9.5.8+有效
5.1.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);
}
[ActionName("MyTest")] // Furion 4.8.4.12+ 支持
public string Test()
{
return nameof(Furion);
}
[HttpGet(Name = "MyTest")] // Furion 4.8.4.12+ 支持,此配置有效的前提是控制器贴有 [Route] 特性
public string Test2()
{
return nameof(Furion);
}
}
}
如下图所示:
5.1.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.1.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.1.9.5 KeepVerb 配置
KeepVerb 参数作用于动作方法,标识是否保留动作谓词。如:
using Furion.DynamicApiController;
namespace Furion.Application
{
public class FurionAppService : IDynamicApiController
{
[ApiDescriptionSettings(KeepVerb = true)]
public string GetVersion()
{
return nameof(Furion);
}
}
}
如下图所示:
5.1.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.1.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.1.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.1.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.1.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;
}
}
}
如下图所示:

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.1.10 DynamicApiControllerSettings 配置
Furion 还提供动态 WebAPI 接口一些全局配置选项,如:
DefaultRoutePrefix:默认路由前缀,string,默认apiDefaultHttpMethod:默认请求谓词,string,默认:POSTDefaultModule:默认模块名称(区域),可用作接口版本,string,默认nullLowercaseRoute:小写路由格式,bool,默认:trueAsLowerCamelCase:启用小驼峰命名(首字母小写),默认falseKeepVerb:是否保留动作谓词,bool,默认:falseKeepName:是否保留默认名称,bool,默认:fasleCamelCaseSeparator:骆驼(驼峰)/帕斯卡命名分隔符,string,默认:-VersionSeparator:版本分隔符,string,默认:v,Furion 4.9.1.14之前是@VersionInFront:版本号是否在前面,bool,默认true,Furion 4.9.1.14+支持ModelToQuery:GET/HEAD请求将类类型参数转查询参数,bool,默认falseSupportedMvcController:是否支持Mvc Controller动态配置,bool,默认falseUrlParameterization:路由参数采用[FromQuery]化,默认false([FromRoute]方式)DefaultArea:配置默认区域,默认nullForceWithRoutePrefix:配置是否强制添加DefaultRoutePrefix,当控制器自定义了[Route]有效,仅限 v3.4.1+版本有效AbandonControllerAffixes:默认去除控制器名称前后缀列表名,string[],默认:AppServicesAppServiceApiControllerControllerServicesService
AbandonActionAffixes:默认去除动作方法名称前后缀列表名,string[],默认:Async
VerbToHttpMethods:复写默认方法名转[HttpMethod]规则,string[][]二维数组类型,内置匹配规则为:["post"] = "POST",["add"] = "POST",["create"] = "POST",["insert"] = "POST",["submit"] = "POST",["get"] = "GET",["find"] = "GET",["fetch"] = "GET",["query"] = "GET",["put"] = "PUT",["update"] = "PUT",["delete"] = "DELETE",["remove"] = "DELETE",["clear"] = "DELETE",["patch"] = "PATCH"- 复写示例
"DynamicApiControllerSettings": {"VerbToHttpMethods": [[ "getall", "HEAD" ], // => getall 会被复写为 `[HttpHead]`[ "other", "PUT" ] // => 新增一条新规则,比如,一 `[other]` 开头会转换为 `[HttpPut]` 请求]}DefaultBindingInfo:默认基元参数绑定方式,string类型,取值route或query,Furion 4.9.2.32+版本支持
5.1.10.1 支持 Mvc 控制器 动态配置
默认情况下,Furion 动态 WebAPI 接口不对 ControllerBase 类型进行任何处理。当然,我们也可以手动启用 ControllerBase 支持。
{
"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] 代替。
5.1.11 关于 AOP 拦截
动态WebAPI 支持 Controller 的所有过滤器/筛选器拦截,也就是可以通过 ActionFilter,ResultFilter 进行拦截操作。如:
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.1.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.1.13 获取路由/控制器/Action 列表
有时候我们需要获取当前路由信息,或所有控制器、Action 列表,而已通过以下代码获取:
- 获取当前路由表信息(简易)
public class MetadataService : IDynamicApiController
{
private readonly IHttpContextAccessor _httpContextAccessor;
public MetadataService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public RouteValueDictionary Print()
{
var routeValuesFeature = _httpContextAccessor.HttpContext.Features.Get<IRouteValuesFeature>();
// 获取路由信息
var routeValues = routeValuesFeature.RouteValues;
return routeValues;
}
}
输出:
{
"action": "print",
"controller": "metadata"
}
- 获取当前终点路由信息(简易)
public class MetadataService : IDynamicApiController
{
private readonly IHttpContextAccessor _httpContextAccessor;
public MetadataService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
[Description("这是一段描述")]
public void Print()
{
var endpointFeature = _httpContextAccessor.HttpContext.Features.Get<IEndpointFeature>();
// 获取路由终点信息
var routeEndpoint = endpointFeature.Endpoint as RouteEndpoint;
var displayName = routeEndpoint.DisplayName; // 路由映射方法 FullName
var routePattern = routeEndpoint.RoutePattern; // 路由表达式(路径)
// 获取路由元数据(特性)
var metadata = routeEndpoint.Metadata;
var attribute = metadata.GetMetadata<DescriptionAttribute>(); // 获取 [Description] 特性
}
}
- 获取所有控制器列表
public class MetadataService : IDynamicApiController
{
private readonly ApplicationPartManager _applicationPartManager;
public MetadataService(ApplicationPartManager applicationPartManager)
{
_applicationPartManager = applicationPartManager;
}
public void Print()
{
var controllerFeature = new ControllerFeature();
_applicationPartManager.PopulateFeature(controllerFeature);
// 获取所有控制器列表
IList<TypeInfo> controllers = controllerFeature.Controllers;
}
}
- 获取所有
Action列表(强大)
public class MetadataService : IDynamicApiController
{
private readonly IActionDescriptorCollectionProvider _actionDescriptorCollectionProvider;
public MetadataService(IActionDescriptorCollectionProvider actionDescriptorCollectionProvider)
{
_actionDescriptorCollectionProvider = actionDescriptorCollectionProvider;
}
public void Print()
{
// 获取所有 Action 列表
var actionDescriptors = _actionDescriptorCollectionProvider.ActionDescriptors.Items;
foreach (ActionDescriptor actionDescriptor in actionDescriptors)
{
// 获取请求的方法
var method = (actionDescriptor as ControllerActionDescriptor).MethodInfo;
// 获取路由地址
var route = actionDescriptor.AttributeRouteInfo.Template;
// 获取 HttpMethod
var httpMethod = actionDescriptor.ActionConstraints?.OfType<HttpMethodActionConstraint>().FirstOrDefault()?.HttpMethods.First();
// 任何关于这个路由/方法/控制器/特性的信息都有
}
}
}
- 获取所有
Action列表带分组信息(强大)
public class MetadataService : IDynamicApiController
{
private readonly IApiDescriptionGroupCollectionProvider _apiDescriptionGroupCollectionProvider;
public MetadataService(IApiDescriptionGroupCollectionProvider apiDescriptionGroupCollectionProvider)
{
_apiDescriptionGroupCollectionProvider = apiDescriptionGroupCollectionProvider;
}
public void Print()
{
// 获取所有控制器列表
var apiDescriptionGroups = _apiDescriptionGroupCollectionProvider.ApiDescriptionGroups.Items;
foreach (ApiDescriptionGroup group in apiDescriptionGroups)
{
// 获取当前分组的所有 Actions
var actions = group.Items;
foreach (ApiDescription action in actions)
{
// 路由地址
var route = action.RelativePath;
// HttpMethod
var httpMethod = action.HttpMethod;
// 分组名
var groupName = action.GroupName;
// Action 描述器
var actionDescriptor = action.ActionDescriptor;
// 获取请求的方法
var method = (actionDescriptor as ControllerActionDescriptor).MethodInfo;
// 任何关于这个路由/方法/控制器/特性的信息都有
}
}
}
}
5.1.14 插件化 IDynamicApiRuntimeChangeProvider
以下内容仅限 Furion 4.8.8.8 + 版本使用。
在一些特定的需求中,我们需要在运行时动态编译代码,如动态编写 WebAPI,之后能够在不重启主机服务的情况下即可有效。比如这里动态添加 SomeClass 动态 WebAPI,然后在 Swagger/路由系统 中立即有效:
using Furion;
using Furion.DynamicApiController;
using Microsoft.AspNetCore.Mvc;
namespace YourProject.Application;
public class PluginApiServices : IDynamicApiController
{
private readonly IDynamicApiRuntimeChangeProvider _provider;
public PluginApiServices(IDynamicApiRuntimeChangeProvider provider)
{
_provider = provider;
}
/// <summary>
/// 动态添加 WebAPI/Controller
/// </summary>
/// <param name="csharpCode"></param>
/// <param name="assemblyName">可自行指定程序集名称</param>
/// <returns></returns>
public string Compile([FromBody] string csharpCode, [FromQuery] string assemblyName = default)
{
// 编译 C# 代码并返回动态程序集
var dynamicAssembly = App.CompileCSharpClassCode(csharpCode, assemblyName);
// 将程序集添加进动态 WebAPI 应用部件
_provider.AddAssembliesWithNotifyChanges(dynamicAssembly);
// 返回动态程序集名称
return dynamicAssembly.GetName().Name;
}
/// <summary>
/// 移除动态程序集 WebAPI/Controller
/// </summary>
public void Remove(string assemblyName)
{
_provider.RemoveAssembliesWithNotifyChanges(assemblyName);
}
}
这时只需要请求 api/plugin-api/compile 接口同时设置请求 Content-Type 为 text/plain,接下来传入 C# 代码字符串 即可,如:
using Furion.DynamicApiController;
namespace YourProject.Application;
public class SomeClass : IDynamicApiController
{
public string GetName()
{
return nameof(Furion);
}
}
之后刷新浏览器即可看到最新的 API:
还可以在运行时动态卸载,使用 DELETE 请求 api/plugin-api 即可。
5.1.15 自定义生成 API 规则
有时候,我们可能希望自定义动态 WebAPI 生成的规则,这时候可以通过下列方式实现:
// 配置动态 WebAPI 选项
services.ConfigureDynamicApiController(builder =>
{
// 自定义控制器生成逻辑,返回 true 时生成控制器,否则不生成
builder.ControllerFilter = (type) => // Furion 4.9.7.133 及以下版本类型为 ControllerModel
{
// 示例:排除 PersonService 类型
return type != typeof(PersonService);
};
// 自定义 Action 生成配置
builder.ActionConfigure = (actionModel) =>
{
// 示例:排除名为 Insert 的方法
if (actionModel.ActionMethod.Name == "Insert")
{
actionModel.ApiExplorer.IsVisible = false;
}
};
});
注意:请确保在 AddInejct() 或 AddDynamicApiControllers 之前注册。
5.1.16 反馈与建议
给 Furion 提 Issue。