当前位置: 首页 > news >正文

第四章:动态WebAPI开发

第四章:动态WebAPI开发

目录

  1. 动态WebAPI概述
  2. IDynamicApiController接口
  3. DynamicApiController特性标记
  4. 路由规则与约定
  5. HTTP方法约定
  6. 自定义路由规则
  7. API版本管理
  8. Swagger文档集成与配置
  9. 请求参数绑定
  10. 返回值处理
  11. 接口分组与标签
  12. 动态WebAPI高级配置

1. 动态WebAPI概述

1.1 什么是动态WebAPI

动态WebAPI是Furion框架最核心、最具特色的功能之一。它允许开发者 无需手动编写Controller类,只需创建普通的服务类,框架便能在运行时自动将其转换为标准的ASP.NET Core Web API控制器。

传统开发方式 vs Furion动态WebAPI:

// ============ 传统方式 ============
// 需要创建Controller,编写大量模板代码
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{private readonly IUserService _userService;public UserController(IUserService userService){_userService = userService;}[HttpGet]public async Task<ActionResult<List<UserDto>>> GetUsers(){var users = await _userService.GetUsersAsync();return Ok(users);}[HttpGet("{id}")]public async Task<ActionResult<UserDto>> GetUser(int id){var user = await _userService.GetUserByIdAsync(id);return Ok(user);}[HttpPost]public async Task<ActionResult<int>> CreateUser(CreateUserInput input){var id = await _userService.CreateUserAsync(input);return Ok(id);}
}// 还需要定义Service接口和实现
public interface IUserService
{Task<List<UserDto>> GetUsersAsync();Task<UserDto> GetUserByIdAsync(int id);Task<int> CreateUserAsync(CreateUserInput input);
}public class UserService : IUserService
{// 实现...
}
// ============ Furion动态WebAPI方式 ============
// 只需一个类,自动生成API接口
public class UserService : IDynamicApiController
{public async Task<List<UserDto>> GetUsersAsync(){// 自动映射为 GET /api/user/usersreturn await _repository.AsQueryable().ToListAsync();}public async Task<UserDto> GetUserByIdAsync(int id){// 自动映射为 GET /api/user/user-by-id?id={id}return await _repository.FindAsync(id);}public async Task<int> CreateUserAsync(CreateUserInput input){// 自动映射为 POST /api/user/create-uservar entity = await _repository.InsertNowAsync(input.Adapt<User>());return entity.Entity.Id;}
}

1.2 动态WebAPI的优势

优势 说明
减少代码量 无需编写Controller、无需手动定义路由
消除重复 不再需要Controller到Service的参数传递代码
自动路由 根据方法名自动推断HTTP方法和路由
Swagger集成 自动生成完整的API文档
灵活配置 支持自定义路由、分组、版本等
兼容性好 生成的是标准ASP.NET Core控制器,完全兼容现有生态

1.3 工作原理

动态WebAPI的工作原理是在应用启动时,Furion会扫描所有实现了IDynamicApiController接口或标记了[DynamicApiController]特性的类,然后通过ASP.NET Core的ApplicationPartApplicationModel机制,动态将这些类注册为控制器。

应用启动│├── 扫描程序集│   └── 查找IDynamicApiController / [DynamicApiController]│├── 解析方法信息│   ├── 方法名 → HTTP方法 + 路由│   ├── 参数 → 请求参数绑定│   └── 返回值 → 响应类型│├── 创建ControllerModel│   └── 注册到ASP.NET Core路由系统│└── API可用└── Swagger文档自动生成

2. IDynamicApiController接口

2.1 基本用法

IDynamicApiController是一个标记接口(Marker Interface),不包含任何方法定义,实现该接口的类会被自动识别为动态API控制器:

using Furion.DynamicApiController;namespace MyApp.Application.Services;/// <summary>
/// 产品管理服务
/// </summary>
public class ProductService : IDynamicApiController
{/// <summary>/// 获取所有产品/// </summary>/// <returns>产品列表</returns>public List<ProductDto> GetAll(){return new List<ProductDto>{new ProductDto { Id = 1, Name = "笔记本电脑", Price = 5999 },new ProductDto { Id = 2, Name = "机械键盘", Price = 399 },new ProductDto { Id = 3, Name = "无线鼠标", Price = 129 }};}/// <summary>/// 根据ID获取产品/// </summary>/// <param name="id">产品ID</param>/// <returns>产品信息</returns>public ProductDto GetById(int id){return new ProductDto { Id = id, Name = "示例产品", Price = 99 };}/// <summary>/// 创建产品/// </summary>/// <param name="input">产品信息</param>/// <returns>新产品ID</returns>public int CreateProduct(CreateProductInput input){// 业务逻辑...return 1;}/// <summary>/// 更新产品/// </summary>/// <param name="id">产品ID</param>/// <param name="input">更新信息</param>public void UpdateProduct(int id, UpdateProductInput input){// 业务逻辑...}/// <summary>/// 删除产品/// </summary>/// <param name="id">产品ID</param>public void DeleteProduct(int id){// 业务逻辑...}
}

上述代码自动生成以下API接口:

HTTP方法 路由 对应方法
GET /api/product/all GetAll()
GET /api/product/by-id?id={id} GetById(int id)
POST /api/product/create-product CreateProduct(...)
PUT /api/product/update-product?id={id} UpdateProduct(...)
DELETE /api/product/delete-product?id={id} DeleteProduct(...)

2.2 构造函数注入

动态API控制器完全支持构造函数依赖注入:

public class OrderService : IDynamicApiController
{private readonly IRepository<Order> _orderRepo;private readonly ILogger<OrderService> _logger;private readonly IMapper _mapper;public OrderService(IRepository<Order> orderRepo,ILogger<OrderService> logger,IMapper mapper){_orderRepo = orderRepo;_logger = logger;_mapper = mapper;}public async Task<List<OrderDto>> GetAllAsync(){_logger.LogInformation("正在查询所有订单");var orders = await _orderRepo.AsQueryable().ToListAsync();return _mapper.Map<List<OrderDto>>(orders);}
}

2.3 排除方法

如果不希望某个公开方法被暴露为API接口,可以使用[NonAction]特性:

using Microsoft.AspNetCore.Mvc;public class HelperService : IDynamicApiController
{// ✅ 这个方法会暴露为APIpublic string GetVersion(){return "1.0.0";}// ❌ 这个方法不会暴露为API[NonAction]public string InternalHelper(){return "内部辅助方法";}
}

3. DynamicApiController特性标记

3.1 使用特性标记

除了实现IDynamicApiController接口外,还可以使用[DynamicApiController]特性来标记动态API控制器:

using Furion.DynamicApiController;/// <summary>
/// 使用特性标记方式
/// </summary>
[DynamicApiController]
public class CategoryService
{public List<string> GetCategories(){return new List<string> { "电子产品", "家居用品", "服装鞋帽" };}public string GetCategory(int id){return $"分类 {id}";}
}

3.2 两种方式的对比

特性 IDynamicApiController接口 [DynamicApiController]特性
使用方式 实现接口 添加特性
侵入性 需要引用Furion 需要引用Furion
继承传递 子类自动继承 子类不自动继承
适用场景 推荐用于标准服务类 适用于不想改变继承关系的类
语义性 语义更强 更灵活

3.3 基类方式

可以创建一个基类来统一实现IDynamicApiController接口:

/// <summary>
/// 应用服务基类
/// </summary>
public abstract class BaseAppService : IDynamicApiController
{/// <summary>/// 获取当前登录用户ID/// </summary>protected long CurrentUserId => App.User?.FindFirst("UserId")?.Value.ToLong() ?? 0;/// <summary>/// 获取当前租户ID/// </summary>protected long CurrentTenantId => App.User?.FindFirst("TenantId")?.Value.ToLong() ?? 0;
}/// <summary>
/// 订单服务 - 继承基类,自动成为动态API
/// </summary>
public class OrderAppService : BaseAppService
{public List<OrderDto> GetMyOrders(){var userId = CurrentUserId;// 查询当前用户的订单...return new List<OrderDto>();}
}

4. 路由规则与约定

4.1 默认路由规则

Furion的动态WebAPI有一套完善的默认路由生成规则:

规则1:控制器名称

类名会被自动处理为路由前缀:

  • 移除常见后缀:ServiceAppServiceApplication
  • 转换为小写kebab-case格式
UserService          → /api/user/...
ProductAppService    → /api/product/...
OrderApplication     → /api/order/...
CategoryService      → /api/category/...

规则2:方法名称转换

方法名会经过以下处理:

  • 移除HTTP方法前缀(Get、Post、Put、Delete等)
  • 移除Async后缀
  • 转换为小写kebab-case格式
public class UserService : IDynamicApiController
{// GET /api/user/allpublic List<User> GetAll() { }// GET /api/user/by-idpublic User GetById(int id) { }// POST /api/userpublic void Post(CreateUserInput input) { }// PUT /api/user/infopublic void UpdateInfo(UpdateUserInput input) { }// DELETE /api/userpublic void Delete(int id) { }// GET /api/user/active-users(异步方法自动移除Async后缀)public Task<List<User>> GetActiveUsersAsync() { }
}

4.2 路由转换规则详解

方法名 转换后路由 HTTP方法
GetAll /all GET
GetById /by-id GET
GetUserInfo /user-info GET
PostUser /user POST
CreateOrder /create-order POST
UpdateUserName /update-user-name PUT
DeleteById /by-id DELETE
GetActiveUsersAsync /active-users GET
Post / POST
Get / GET

4.3 PascalCase到kebab-case的转换

Furion会自动将方法名从PascalCase转换为kebab-case:

GetUserList       → get-user-list    → /api/user/user-list (去掉Get前缀)
CreateNewOrder    → create-new-order → /api/order/create-new-order
FindByNameAndAge  → find-by-name-and-age
UpdateUserProfile → update-user-profile

4.4 默认路由模板

默认的路由模板为:

api/[controller]/[action]

其中:

  • api 是固定前缀
  • [controller] 是控制器名称(类名去掉后缀)
  • [action] 是操作名称(方法名去掉HTTP前缀)

5. HTTP方法约定

5.1 方法名前缀约定

Furion通过方法名的前缀来自动推断HTTP方法:

方法名前缀 对应HTTP方法 常见用途
GetFindFetchQuerySearch GET 查询数据
PostCreateAddInsertSubmit POST 创建数据
PutUpdateModifyChangeEdit PUT 更新数据
DeleteRemoveClearCancel DELETE 删除数据
Patch PATCH 部分更新
public class ArticleService : IDynamicApiController
{// ===== GET 方法 =====public List<Article> GetAll() { }              // GET /api/article/allpublic Article FindById(int id) { }            // GET /api/article/by-idpublic List<Article> FetchLatest() { }         // GET /api/article/latestpublic List<Article> QueryByCategory(int c) {} // GET /api/article/by-categorypublic List<Article> SearchByTitle(string t) {} // GET /api/article/by-title// ===== POST 方法 =====public int CreateArticle(ArticleInput i) { }   // POST /api/article/articlepublic int AddComment(CommentInput i) { }      // POST /api/article/commentpublic void SubmitReview(int id) { }            // POST /api/article/review// ===== PUT 方法 =====public void UpdateTitle(int id, string t) { }  // PUT /api/article/titlepublic void ModifyContent(int id, string c) { } // PUT /api/article/contentpublic void EditArticle(ArticleInput i) { }     // PUT /api/article/article// ===== DELETE 方法 =====public void DeleteArticle(int id) { }           // DELETE /api/article/articlepublic void RemoveComment(int id) { }           // DELETE /api/article/commentpublic void ClearDrafts() { }                    // DELETE /api/article/drafts
}

5.2 显式指定HTTP方法

如果默认约定不满足需求,可以使用ASP.NET Core的HTTP方法特性显式指定:

using Microsoft.AspNetCore.Mvc;public class PaymentService : IDynamicApiController
{// 显式指定为POST(虽然以Get开头)[HttpPost]public PaymentResult GetPaymentUrl(PaymentInput input){// 获取支付链接通常需要POSTreturn new PaymentResult { Url = "https://pay.example.com/..." };}// 显式指定为GET(虽然以Check开头,默认会是POST)[HttpGet]public bool CheckPaymentStatus(string orderId){return true;}// 方法名不以约定前缀开头时,默认为POST// 可以显式指定为其他方法[HttpPut]public void Process(int orderId){// 处理订单}
}

5.3 无前缀方法名的默认行为

如果方法名不以任何约定前缀开头,Furion默认将其视为 POST 方法:

public class TaskService : IDynamicApiController
{public void Execute(int taskId) { }       // POST /api/task/executepublic string Process(string data) { }    // POST /api/task/processpublic bool Validate(ValidateInput i) { } // POST /api/task/validate
}

6. 自定义路由规则

6.1 使用ApiDescriptionSettings自定义控制器路由

[ApiDescriptionSettings]特性允许自定义控制器级别的路由配置:

using Furion.DynamicApiController;
using Microsoft.AspNetCore.Mvc;// 自定义控制器名称
[ApiDescriptionSettings(Name = "Users")]
public class UserManagementService : IDynamicApiController
{// 生成路由:GET /api/users/list 而不是 /api/user-management/listpublic List<UserDto> GetList() { return new(); }
}// 自定义路由前缀
[ApiDescriptionSettings(Name = "Admin/Users")]
public class AdminUserService : IDynamicApiController
{// 生成路由:GET /api/admin/users/listpublic List<UserDto> GetList() { return new(); }
}

6.2 使用Route特性自定义路由

可以使用ASP.NET Core标准的[Route]特性来完全控制路由:

public class CustomRouteService : IDynamicApiController
{// 完全自定义路由[HttpGet("api/v1/users/{id}")]public UserDto GetUserById(int id){return new UserDto { Id = id, Name = "自定义路由用户" };}// 使用路由模板参数[HttpGet("api/departments/{deptId}/users")]public List<UserDto> GetDepartmentUsers(int deptId){return new List<UserDto>();}// 多路由映射到同一方法[HttpGet("api/users/search")][HttpGet("api/users/find")]public List<UserDto> SearchUsers(string keyword){return new List<UserDto>();}
}

6.3 配置全局路由前缀

appsettings.json中可以配置全局路由前缀:

{"DynamicApiControllerSettings": {"DefaultRoutePrefix": "api","DefaultHttpMethod": "POST","LowercaseRoute": true,"KeepVerb": false,"KeepName": false,"AsLowerCamelCase": false,"SplitCamelCase": true,"AbandonControllerAffixes": ["Service", "AppService", "Application"],"AbandonActionAffixes": ["Async"]}
}
配置项 默认值 说明
DefaultRoutePrefix "api" 全局路由前缀
DefaultHttpMethod "POST" 无法推断HTTP方法时的默认方法
LowercaseRoute true 路由是否转为小写
KeepVerb false 是否保留方法名中的HTTP动词前缀
KeepName false 是否保留完整方法名
SplitCamelCase true 是否将PascalCase拆分为kebab-case
AbandonControllerAffixes [...] 要移除的控制器名称后缀
AbandonActionAffixes ["Async"] 要移除的方法名后缀

6.4 路由规则示例对照

public class ProductService : IDynamicApiController
{// KeepVerb=false(默认): GET /api/product/list// KeepVerb=true:         GET /api/product/get-listpublic List<Product> GetList() { }// KeepName=false(默认): POST /api/product/product// KeepName=true:         POST /api/product/create-productpublic int CreateProduct(ProductInput input) { }// SplitCamelCase=true(默认): GET /api/product/by-category-id// SplitCamelCase=false:        GET /api/product/bycategoryidpublic List<Product> GetByCategoryId(int categoryId) { }
}

7. API版本管理

7.1 基于路由的版本管理

// V1版本
[ApiDescriptionSettings(Name = "v1/User")]
public class UserServiceV1 : IDynamicApiController
{// GET /api/v1/user/infopublic UserDto GetInfo(int id){return new UserDto { Id = id, Name = "V1接口" };}
}// V2版本
[ApiDescriptionSettings(Name = "v2/User")]
public class UserServiceV2 : IDynamicApiController
{// GET /api/v2/user/infopublic UserDtoV2 GetInfo(int id){return new UserDtoV2 { Id = id, Name = "V2接口", Avatar = "avatar.png" };}
}

7.2 基于分组的版本管理

// V1分组
[ApiDescriptionSettings("V1")]
public class UserServiceV1 : IDynamicApiController
{public UserDto GetInfo(int id) { return new(); }
}// V2分组
[ApiDescriptionSettings("V2")]
public class UserServiceV2 : IDynamicApiController
{public UserDtoV2 GetInfo(int id) { return new(); }
}

在Swagger中会根据分组分别显示不同版本的接口。

7.3 集成ASP.NET Core API版本控制

// 安装版本控制包
// dotnet add package Microsoft.AspNetCore.Mvc.Versioning// 在Startup中配置
services.AddApiVersioning(options =>
{options.DefaultApiVersion = new ApiVersion(1, 0);options.AssumeDefaultVersionWhenUnspecified = true;options.ReportApiVersions = true;
});

8. Swagger文档集成与配置

8.1 基本Swagger配置

Furion通过AddInject()UseInject()自动集成了Swagger。默认配置下,访问应用根路径即可看到Swagger UI。

8.2 详细Swagger配置

// appsettings.json
{"SpecificationDocumentSettings": {"DocumentTitle": "MyApp API 文档","DefaultGroupName": "Default","FormatAsV2": false,"DocExpansionState": "List","XmlComments": true,"RoutePrefix": "api-docs","ServerDir": "","LoginInfo": {"Enabled": true},"GroupOpenApiInfos": [{"Group": "Default","Title": "通用接口","Description": "通用业务接口","Version": "v1.0","TermsOfService": "https://furion.net","Contact": {"Name": "开发团队","Email": "dev@example.com","Url": "https://example.com"}},{"Group": "System","Title": "系统管理接口","Description": "系统管理和配置相关接口","Version": "v1.0"}],"EnableAuthorized": true,"SecurityDefinitions": [{"Id": "Bearer","Type": "Http","Name": "Authorization","Description": "JWT授权。在下方输入Token(不需要加Bearer前缀)","BearerFormat": "JWT","Scheme": "bearer","In": "Header"}]}
}

8.3 XML注释显示

确保在所有包含动态API的项目中启用XML文档生成:

<!-- 在每个项目的.csproj中添加 -->
<PropertyGroup><GenerateDocumentationFile>true</GenerateDocumentationFile><NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
/// <summary>
/// 订单管理服务
/// </summary>
public class OrderService : IDynamicApiController
{/// <summary>/// 创建订单/// </summary>/// <remarks>/// 请求示例://////     POST /api/order/create///     {///         "productId": 1,///         "quantity": 2,///         "address": "北京市朝阳区"///     }////// </remarks>/// <param name="input">订单创建参数</param>/// <returns>订单编号</returns>/// <response code="200">创建成功</response>/// <response code="400">参数验证失败</response>public string CreateOrder(CreateOrderInput input){return "ORD20240115001";}
}

9. 请求参数绑定

9.1 参数绑定特性

Furion支持ASP.NET Core的所有参数绑定方式:

特性 说明 适用场景
[FromQuery] 从URL查询字符串获取 GET请求的简单参数
[FromBody] 从请求体获取 POST/PUT的复杂对象
[FromRoute] 从路由模板获取 路径参数
[FromHeader] 从请求头获取 认证信息、自定义头
[FromForm] 从表单数据获取 文件上传、表单提交

9.2 默认绑定规则

Furion对不同HTTP方法有不同的默认参数绑定规则:

public class BindingDemoService : IDynamicApiController
{// GET方法:简单类型默认从Query获取// GET /api/binding-demo/user?id=1&name=张三public UserDto GetUser(int id, string name){// id 和 name 自动从QueryString获取return new UserDto { Id = id, Name = name };}// POST方法:复杂类型默认从Body获取// POST /api/binding-demo/user// Body: { "name": "张三", "email": "..." }public int CreateUser(CreateUserInput input){// input 自动从RequestBody获取return 1;}// PUT方法:复杂类型默认从Body获取// PUT /api/binding-demo/user?id=1// Body: { "name": "新名称" }public void UpdateUser(int id, UpdateUserInput input){// id 从QueryString获取// input 从RequestBody获取}
}

9.3 显式指定参数绑定

using Microsoft.AspNetCore.Mvc;public class ExplicitBindingService : IDynamicApiController
{// 从路由获取参数[HttpGet("api/users/{userId}/orders/{orderId}")]public OrderDto GetOrder([FromRoute] int userId,[FromRoute] int orderId){return new OrderDto();}// 从请求头获取参数[HttpGet]public UserDto GetCurrentUser([FromHeader(Name = "Authorization")] string token,[FromHeader(Name = "X-Tenant-Id")] string tenantId){return new UserDto();}// 混合绑定[HttpPost("api/products/{categoryId}")]public int CreateProduct([FromRoute] int categoryId,[FromQuery] string source,[FromBody] CreateProductInput input){return 1;}// 文件上传[HttpPost]public string UploadFile([FromForm] string description,[FromForm] IFormFile file){return file.FileName;}
}

9.4 复杂查询参数

// 分页查询DTO
public class PagedQueryInput
{public int PageIndex { get; set; } = 1;public int PageSize { get; set; } = 20;public string Keyword { get; set; }public string OrderBy { get; set; }public bool IsDesc { get; set; }
}public class UserQueryService : IDynamicApiController
{// GET /api/user-query/paged?pageIndex=1&pageSize=20&keyword=张// 复杂类型在GET方法中需要显式标记[FromQuery]public PagedResult<UserDto> GetPaged([FromQuery] PagedQueryInput input){return new PagedResult<UserDto>{Total = 100,PageIndex = input.PageIndex,PageSize = input.PageSize,Items = new List<UserDto>()};}
}

10. 返回值处理

10.1 基本返回类型

Furion的动态WebAPI支持各种返回类型:

public class ReturnDemoService : IDynamicApiController
{// 返回简单类型public string GetString() => "Hello";public int GetNumber() => 42;public bool GetBool() => true;// 返回对象public UserDto GetUser(){return new UserDto { Id = 1, Name = "张三" };}// 返回集合public List<UserDto> GetUsers(){return new List<UserDto>();}// 返回匿名对象public object GetDashboard(){return new{UserCount = 1000,OrderCount = 5000,Revenue = 1234567.89};}// 无返回值(void)public void PostAction(){// 返回204 No Content}// 返回Taskpublic async Task PostAsyncAction(){await Task.Delay(100);}// 返回Task<T>public async Task<UserDto> GetUserAsync(){return await Task.FromResult(new UserDto());}
}

10.2 规范化结果

Furion提供了规范化结果功能,将所有API返回值包装为统一格式:

// 启用规范化结果
services.AddControllers().AddInject();// 规范化返回格式示例:
// {
//     "statusCode": 200,
//     "succeeded": true,
//     "data": { ... },       // 实际返回数据
//     "errors": null,
//     "extras": null,
//     "timestamp": 1705312000
// }

自定义规范化结果提供器:

using Furion.DataValidation;
using Furion.UnifyResult;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;/// <summary>
/// 自定义规范化结果提供器
/// </summary>
[UnifyModel(typeof(ApiResult<>))]
public class CustomResultProvider : IUnifyResultProvider
{public IActionResult OnSucceeded(ActionExecutedContext context, object data){return new JsonResult(new ApiResult<object>{Code = 200,Success = true,Message = "请求成功",Data = data});}public IActionResult OnValidateFailed(ActionExecutingContext context, ValidationMetadata metadata){return new JsonResult(new ApiResult<object>{Code = 400,Success = false,Message = metadata.Message,Data = null});}public async Task OnResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings = null){switch (statusCode){case 401:await context.Response.WriteAsJsonAsync(new ApiResult<object>{Code = 401,Success = false,Message = "未授权",Data = null});break;case 403:await context.Response.WriteAsJsonAsync(new ApiResult<object>{Code = 403,Success = false,Message = "禁止访问",Data = null});break;}}public IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata){return new JsonResult(new ApiResult<object>{Code = 500,Success = false,Message = metadata.Message,Data = null});}
}/// <summary>
/// 统一返回结果模型
/// </summary>
public class ApiResult<T>
{public int Code { get; set; }public bool Success { get; set; }public string Message { get; set; }public T Data { get; set; }
}

10.3 跳过规范化结果

某些接口不需要规范化结果包装时,可以使用特性跳过:

using Furion.UnifyResult;public class FileService : IDynamicApiController
{// 文件下载不需要规范化结果包装[NonUnify][HttpGet]public IActionResult DownloadFile(string fileName){var bytes = System.IO.File.ReadAllBytes($"uploads/{fileName}");return new FileContentResult(bytes, "application/octet-stream"){FileDownloadName = fileName};}// 健康检查不需要包装[NonUnify]public string HealthCheck() => "OK";
}

11. 接口分组与标签

11.1 使用ApiDescriptionSettings分组

// 系统管理分组
[ApiDescriptionSettings("System", Name = "SysUser", Order = 1)]
public class SysUserService : IDynamicApiController
{/// <summary>/// 获取系统用户列表/// </summary>public List<UserDto> GetList() => new();/// <summary>/// 创建系统用户/// </summary>public int Create(CreateUserInput input) => 1;
}// 业务管理分组
[ApiDescriptionSettings("Business", Name = "Order", Order = 1)]
public class OrderService : IDynamicApiController
{/// <summary>/// 获取订单列表/// </summary>public List<OrderDto> GetList() => new();
}// 报表分组
[ApiDescriptionSettings("Report", Name = "SalesReport", Order = 1)]
public class SalesReportService : IDynamicApiController
{/// <summary>/// 获取销售报表/// </summary>public object GetSalesReport() => new { };
}

11.2 Swagger分组配置

{"SpecificationDocumentSettings": {"GroupOpenApiInfos": [{"Group": "Default","Title": "默认分组","Description": "未分类的接口","Version": "v1.0"},{"Group": "System","Title": "系统管理","Description": "系统管理相关接口,包括用户、角色、菜单等","Version": "v1.0"},{"Group": "Business","Title": "业务管理","Description": "核心业务相关接口","Version": "v1.0"},{"Group": "Report","Title": "报表统计","Description": "数据报表和统计分析接口","Version": "v1.0"}]}
}

11.3 方法级别的分组

public class MixedService : IDynamicApiController
{// 同一个服务中的方法可以属于不同分组[ApiDescriptionSettings("System")]public string GetSystemInfo() => "系统信息";[ApiDescriptionSettings("Business")]public string GetBusinessData() => "业务数据";// 可以同时出现在多个分组中[ApiDescriptionSettings("System,Business")]public string GetCommonData() => "公共数据";
}

11.4 排序控制

// 通过Order控制接口在Swagger中的显示顺序
[ApiDescriptionSettings("System", Order = 1)]  // 第一个显示
public class UserService : IDynamicApiController { }[ApiDescriptionSettings("System", Order = 2)]  // 第二个显示
public class RoleService : IDynamicApiController { }[ApiDescriptionSettings("System", Order = 3)]  // 第三个显示
public class MenuService : IDynamicApiController { }

12. 动态WebAPI高级配置

12.1 控制器命名规则配置

// 自定义控制器名称
[ApiDescriptionSettings(Name = "UserMgmt",           // 自定义名称Tag = "用户管理",             // Swagger标签Order = 1,                   // 排序Description = "用户管理相关接口" // 描述
)]
public class UserManagementService : IDynamicApiController
{public List<UserDto> GetList() => new();
}

12.2 隐藏接口

public class InternalService : IDynamicApiController
{// 公开接口public string GetPublicData() => "公开数据";// 隐藏接口(在Swagger中不显示,但仍可访问)[ApiDescriptionSettings(false)]public string GetHiddenData() => "隐藏数据";
}

12.3 自定义模型绑定

// 自定义日期格式绑定
public class DateRangeInput
{[ModelBinder(BinderType = typeof(DateTimeModelBinder))]public DateTime StartDate { get; set; }[ModelBinder(BinderType = typeof(DateTimeModelBinder))]public DateTime EndDate { get; set; }
}

12.4 动态WebAPI与传统Controller共存

Furion的动态WebAPI可以与传统的ASP.NET Core Controller共存,互不影响:

// 传统Controller
[ApiController]
[Route("api/[controller]")]
public class LegacyController : ControllerBase
{[HttpGet]public IActionResult Get() => Ok("传统Controller");
}// 动态WebAPI
public class ModernService : IDynamicApiController
{public string GetData() => "动态WebAPI";
}// 两者可以在同一个项目中同时使用
// GET /api/legacy         → 传统Controller
// GET /api/modern/data    → 动态WebAPI

12.5 性能优化建议

建议 说明
合理使用异步方法 IO密集型操作使用async/await
避免大对象返回 使用分页和投影减少返回数据量
利用缓存 频繁查询的数据使用缓存
压缩响应 启用gzip/brotli压缩
限制请求大小 配置最大请求体大小
// 异步方法示例
public class OptimizedService : IDynamicApiController
{private readonly IRepository<Product> _repo;private readonly IMemoryCache _cache;public OptimizedService(IRepository<Product> repo, IMemoryCache cache){_repo = repo;_cache = cache;}/// <summary>/// 分页查询(高性能)/// </summary>public async Task<PagedResult<ProductDto>> GetPagedAsync([FromQuery] PagedInput input){var cacheKey = $"products:{input.PageIndex}:{input.PageSize}:{input.Keyword}";return await _cache.GetOrCreateAsync(cacheKey, async entry =>{entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);var query = _repo.AsQueryable();if (!string.IsNullOrEmpty(input.Keyword)){query = query.Where(p => p.Name.Contains(input.Keyword));}var total = await query.CountAsync();var items = await query.Skip((input.PageIndex - 1) * input.PageSize).Take(input.PageSize).Select(p => new ProductDto { Id = p.Id, Name = p.Name }).ToListAsync();return new PagedResult<ProductDto>{Total = total,Items = items};});}
}

总结

本章全面介绍了Furion动态WebAPI的开发方法,这是Furion框架最核心的特色功能。通过动态WebAPI,开发者可以大幅减少样板代码,专注于业务逻辑的实现。

关键要点:

  • 实现IDynamicApiController接口或使用[DynamicApiController]特性即可创建动态API
  • 方法名前缀约定自动推断HTTP方法(Get→GET, Create→POST, Update→PUT, Delete→DELETE)
  • 路由自动生成遵循api/[controller]/[action]的模板,支持kebab-case转换
  • 参数绑定遵循HTTP方法的默认规则,也可以显式指定
  • 规范化结果提供统一的API返回格式
  • 接口分组通过ApiDescriptionSettings实现,方便API文档组织

下一章预告:第五章将详细介绍Furion的依赖注入与服务注册机制,包括接口标记注入、构造函数注入、属性注入等多种注入方式。

http://www.gsyq.cn/news/1557382.html

相关文章:

  • 软件价格优化中的动态定价模型
  • MC9S08GT16A/GT8A微控制器:HCS08内核、低功耗模式与硬件设计精解
  • JMeter性能测试实战:从压力测试到瓶颈定位的完整闭环
  • PSP记录练习
  • 主机名配置不当引发的sudo解析故障排查指南 _ 统信UOS _ 麒麟KYLINOS
  • Velero终极指南:5步掌握Kubernetes备份与迁移的完整解决方案
  • IPD不只是流程:解码华为产品从构想到退市的“生命线”
  • 3分钟免费安装VideoDownloadHelper:浏览器视频下载插件终极指南
  • 第七章:数据验证与异常处理
  • 跨平台中文显示一致性解决方案:苹果平方字体全面集成指南
  • S12XS PIM模块深度解析:从GPIO基础到外设引脚重映射实战
  • 2026淮南漏水检测维修精选优质服务商TOP5推荐!卫生间漏水/厨房漏水/屋顶天花板漏水/阳台漏水/地下室漏水防水补漏检测维修-正规防水补漏公司优选口碑榜测评推荐 - 即刻修防水
  • MC9S12VR CPMU_UHV模块深度解析:从PLL配置到低功耗设计的嵌入式时钟管理实战
  • 2026年更新:专业温州高三复读学校的深度选择指南 - 品牌鉴赏官2026
  • 2026滁州2026正规漏水检测维修公司精选口碑榜TOP5权威推荐-精准定位检测漏水点-专业防水补漏堵漏维修、卫生间/厨房/屋顶/天沟/地下室/阳台防水漏水检测维修 - 安佳防水
  • 2026年更新:长沙县建材车间装修公司深度解析与选型指南 - 品牌鉴赏官2026
  • 3个技巧让ZeroBot-Plugin成为你的远程协作效率倍增器
  • 2026昌吉防水补漏维修团队实测盘点:昌吉业主房屋渗漏修缮靠谱选择 - 宅安选房屋修缮
  • ReactOS DelNode: 610 tests executed
  • 广州修补家具大理石/瓷砖/岩板/木门补漆推荐良匠千艺2026本地口碑榜 - 我叫一
  • 2026上海全市 16 区分区上门空调维修推荐,30分钟速达,修不好不收费 - 星际AI
  • 如何快速上手AlphaFold 3:蛋白质结构预测的终极指南
  • 广州大理石修补推荐良匠千艺2026口碑榜 - 我叫一
  • 2026年李沧区专业的厕所疏通服务推荐 - 品牌排行榜
  • 2026深圳本地人必选防水补漏检测维修公司靠谱服务商TOP5推荐:房屋渗漏水检测维修/卫生间/厨房/天花板/阳台/外墙渗漏水检测补漏维修-暗管漏水检测专业仪器精准定位漏水点 - 即刻修防水
  • 多中心医学影像分析:异构集成系统应对COVID-19诊断挑战
  • 告别终端恐惧:Applite如何让macOS软件管理变得像逛商店一样简单
  • 2026 郑州空调维修|管道疏通|水电维修正规公司实力排行榜(权威测评版) - 星际AI
  • 跨平台KVM革命:Input Leap如何用一套键鼠掌控Windows、macOS、Linux多台设备
  • 北京东城区字画回收机构怎么选 2026年实用指南 - 品牌排行榜