API 分层
HTTP 接入层负责协议转换:路由、模型绑定、认证/权限特性、状态码和 Swagger 分组。业务查询、事务和状态变化由 Application Service 或 Domain Manager 完成。
Controller 在哪里
四个主业务模块已将大部分 Controller 拆到独立 HttpApi 项目:
| 业务边界 | Controller 项目 | Core 依赖 |
|---|---|---|
| CmsKit | src/Services/CmsKit/FreeKit.CmsKit.HttpApi | FreeKit.CmsKit |
| Identity | src/Services/Identity/FreeKit.Identity.HttpApi | FreeKit.Identity |
| Member | src/Services/Identity/FreeKit.Member.HttpApi | FreeKit.Member |
| Platform | src/Services/Platform/FreeKit.Platform.HttpApi | FreeKit.Platform |
Identity 还有四个历史上保留在 Infrastructure 项目中的 Controller:文件、公共附件、普通设置和管理设置,位于 FreeKit.Identity.Infrastructure/Application。文档和依赖图应如实保留这个例外,不要把它描述成纯持久化项目。
CmsKit HttpApi 按业务域继续组织:
Controllers/
├── Admin/
├── Community/
├── Content/
├── Discovery/
├── Engagement/
└── Governance/
路由与 Swagger 分组
当前没有全局的 Area 路由生成器。Controller 使用显式 [Route],[ApiExplorerSettings] 只决定 Swagger 分组。
| 模块 | 主要基础路由 | Swagger GroupName | 例外 |
|---|---|---|---|
| Identity / Member | /api/identity/* | identity | 无统一 [Area] 要求 |
| CmsKit 公共端 | /api/cms/* | cms | 部分 Controller 带 [Area("cms")] |
| CmsKit 管理端 | /api/cms/admin/* | cms-admin | 管理权限通常在 Action 上声明 |
| Platform | /api/plat/* | plat | /api/dailyhot、/api/v1/* 是显式历史路由 |
真实示例:
[ApiExplorerSettings(GroupName = "cms")]
[Area("cms")]
[Route("api/cms/articles")]
[ApiController]
public sealed class ArticleController(IArticleService service)
: FreeKitController
{
[HttpGet("{id}")]
[AllowAnonymous]
public Task<ArticleDto> GetAsync(Guid id)
{
return service.GetAsync(id);
}
}
Area、模块 key 和 URL 前缀是独立信息。不要将 Platform 的 Area 写成 platform,也不要假设 [module] 会自动应用到所有 Controller。
PathBase
Host 在端点之前调用:
string pathBase = builder.Configuration
.GetValue<string>("ASPNETCORE_PATHBASE") ?? string.Empty;
app.UsePathBase(new PathString(pathBase));
开发环境主 Host 的 PathBase 是 /kit_api,因此文章地址是 /kit_api/api/cms/articles/{id};Controller 自身的 Route 仍写成 api/cms/articles,不要把部署前缀硬编码进模块。
没有通用 API Versioning
仓库当前没有基于 [ApiVersion]、{version:apiVersion} 的统一版本协商配置。以下路径只是 Controller 写死的 URL:
/api/v1/classic-crypto/api/v1/JSON/api/v1/sm
它们不能证明整个 API 已启用版本控制。新增接口应延续所属模块现有路由;如果未来引入 API Versioning,需要先在 Web BuildingBlock 统一配置,再迁移 Controller。
Controller 基类与认证
KitApiControllerBase只提供[ApiController]。FreeKitController额外提供[Authorize],默认要求登录。- 匿名接口在 Action 或 Controller 上显式使用
[AllowAnonymous]。 - 当前用户和仓储应在 Application Service 中使用,不要向 Controller 基类虚构
CurrentUser属性。
权限策略
仓库同时存在标准 policy 和 KitAuthorizeAttribute 两种写法。
标准 policy 示例来自 Identity:
[HttpGet]
[Authorize(IdentityPermissions.Users.GetList)]
public Task<PagedResultDto<UserDto>> GetListAsync(
[FromQuery] UserQuery query)
{
return userService.GetListAsync(query);
}
KitAuthorize 示例来自 Platform ShortUrl:
[HttpGet]
[KitAuthorize("Platform.ShortUrl", "短链接管理")]
public Task<PagedResultDto<ShortUrlDto>> GetListAsync(
[FromQuery] ShortUrlQuery query)
{
return shortUrlService.GetListAsync(query);
}
授权链路为:
FreeKitController或权限特性要求身份认证。KitAuthorizationPolicyProvider为权限字符串动态创建 policy。ValidTokenHandler校验当前 Token/会话状态。ValidPermissionHandler通过PermissionStore检查用户、角色和岗位授权。
无效 Token 返回 401,Token 有效但缺少权限返回 403。权限扫描同步由 PermissionUtil、PermissionManager.InitPermissionAsync 完成;主 Host 当前只在 DEBUG 下调用 MigrationStartAsync(),生产发布不能依赖启动时自动创建新权限,需在发布流程中显式迁移或初始化。
DTO 与 JSON 契约
Controller 的输入输出使用 Application/Contracts 中的请求和响应 DTO,不直接返回可变领域聚合。模型验证由 [ApiController] 和全局 InvalidModelStateResponseFactory 处理。
API 边界统一使用 Newtonsoft.Json:
- 引用 Newtonsoft 特性,或依赖默认属性命名。
- 不要在公开 DTO 上只写
[JsonPropertyName]等 System.Text.Json 专属特性。 - 外部 HTTP 客户端的内部 DTO 可以独立选择 System.Text.Json。
异步业务方法使用 Async 后缀;Controller Action 可以沿用 ASP.NET Core 命名习惯,但当前新代码也优先保持 Async 以便和 Service 对齐。
SignalR 也属于 HttpApi
CmsKitHttpApiModuleStartup.Configure映射/hubs/notifications。IdentityHttpApiModuleStartup.Configure映射/hubs/onlineuser。
Hub 使用 ValidTokenHubFilter 并通过 Redis 扩展横向广播。后台 Host 只引用 Core 时不会暴露这些端点。
主 Host 文档入口
FreeKit.Host 当前注册五个 Swagger 文档:
v1identityplatcmscms-admin
开发地址分别挂在 https://localhost:7000/kit_api/swagger,RapiDoc 路由为 /kit_api/r。
源码索引
src/BuildingBlocks/IGeekFan.FreeKit.Infrastructure/Application/FreeKitController.cssrc/BuildingBlocks/IGeekFan.FreeKit.Infrastructure/Authorization/KitAuthorizationPolicyProvider.cssrc/BuildingBlocks/IGeekFan.FreeKit.Infrastructure/Authorization/Permissions/KitAuthorizeAttribute.cssrc/Services/Host/FreeKit.Host/Program.cssrc/Services/CmsKit/FreeKit.CmsKit.HttpApi/Controllerssrc/Services/Identity/FreeKit.Identity.HttpApi/Controllerssrc/Services/Platform/FreeKit.Platform.HttpApi