

Also from Kynth Studios


Also from Kynth Studios


Also from Kynth Studios
123456789# 控制器开发规范1011> 📖 API 设计规范(RESTful、响应格式、状态码等)参见 [api-design.mdc](mdc:.cursor/rules/api-design.mdc)1213## 基本要求1415| 要求 | 说明 |16|-----|------|17| 继承基类 | 继承当前项目中的 `ApiControllerBase` |18| 控制器特性 | 必须添加 `[DisplayName]` 和 `[Navigation]` 特性 |19| Action 特性 | 所有方法必须添加 `[DisplayName]` 特性 |20| 返回类型 | 所有方法返回 `ActionResult<ApiResponse<T>>` |21| 异常处理 | 不在 Action 中捕获异常,由统一过滤器处理 |22| 多参数处理 | 多个参数应封装为 DTO 模型 |23| XML 注释 | 控制器类和公共方法应添加 XML 文档注释 |2425## 控制器类型2627### 1. 标准业务控制器2829```csharp30using CodeSpirit.Core;31using CodeSpirit.Core.Attributes;32using CodeSpirit.Core.Enums;33using CodeSpirit.Navigation.Resources;34using Microsoft.AspNetCore.Mvc;35using System.ComponentModel;3637namespace CodeSpirit.IdentityApi.Controllers;3839/// <summary>40/// 用户管理控制器41/// </summary>42[DisplayName("用户管理")]43[Navigation(Icon = "fa-solid fa-users",44 PlatformType = PlatformType.Tenant,45 TitleResourceKey = "Controller.Users",46 TitleResourceType = typeof(NavigationResources))]47public class UsersController : ApiControllerBase48{49 private readonly IUserService _userService;5051 public UsersController(IUserService userService)52 {53 _userService = userService;54 }5556 /// <summary>57 /// 获取用户列表58 /// </summary>59 [HttpGet]60 [DisplayName("获取用户列表")]61 public async Task<ActionResult<ApiResponse<PageList<UserDto>>>> GetUsers(62 [FromQuery] UserQueryDto queryDto)63 {64 PageList<UserDto> users = await _userService.GetUsersAsync(queryDto);65 return SuccessResponse(users);66 }67}68```6970### 2. 设置页面控制器7172用于用户设置、系统配置等场景:7374```csharp75[DisplayName("用户设置")]76[Navigation(Icon = "fa-solid fa-user-cog", Order = 150, PlatformType = PlatformType.Tenant)]77[SettingsPage(Title = "用户设置", Description = "管理用户偏好和系统配置")]78public class UserSettingsController : ApiControllerBase { }79```8081### 3. 内部 API 控制器8283用于微服务间内部通信,不对外暴露:8485```csharp86/// <summary>87/// 内部用户信息访问控制器88/// </summary>89[DisableAggregator] // 禁用 API 聚合90[DisplayName("内部用户信息")]91[Module("default")] // 指定模块92[Route("api/identity/internal/users")] // 自定义路由93[NoAudit("内部 API 不需要审计")]94public class InternalUsersController : ControllerBase { } // 继承 ControllerBase95```9697### 4. 匿名访问控制器9899无需认证的公开 API:100101```csharp102[AllowAnonymous]103[Navigation(Hidden = true)] // 隐藏导航104[NoAudit("授权控制器不需要审计")]105public class AuthController : ApiControllerBase { }106```107108## 控制器特性详解109110### Navigation 特性111112配置导航菜单和权限:113114| 属性 | 类型 | 说明 |115|-----|------|------|116| `Icon` | string | Font Awesome 图标类名 |117| `PlatformType` | PlatformType | 平台类型:`Tenant`(租户端)/ `System`(系统端)|118| `Order` | int | 菜单排序(数字越小越靠前)|119| `Hidden` | bool | 是否隐藏导航菜单 |120| `TitleResourceKey` | string | 多语言标题资源键 |121| `TitleResourceType` | Type | 多语言资源类型 |122123```csharp124// 完整示例125[Navigation(126 Icon = "fa-solid fa-users",127 PlatformType = PlatformType.Tenant,128 Order = 100,129 TitleResourceKey = "Controller.Users",130 TitleResourceType = typeof(NavigationResources))]131```132133### Audit 特性134135控制审计日志记录:136137```csharp138// 启用审计139[Audit(EntityName = nameof(Department), LogRequestParams = true, LogResponseData = true)]140141// 禁用审计142[NoAudit("授权控制器不需要审计")]143```144145### 其他控制器特性146147| 特性 | 用途 |148|-----|------|149| `[Module("name")]` | 指定模块名称,用于路由分组 |150| `[DisableAggregator]` | 禁用 API 聚合,用于内部 API |151| `[SettingsPage]` | 标记为设置页面控制器 |152| `[AllowAnonymous]` | 允许匿名访问 |153| `[Authorize]` | 显式要求认证 |154155## 操作特性156157除标准 CRUD 操作外,其他操作应添加 `Operation` 或其派生特性:158159### 操作特性类型160161| 特性 | 用途 | 典型场景 |162|-----|------|---------|163| `[Operation]` | 基础操作 | 行级操作(解锁、禁用、删除等)|164| `[HeaderOperation]` | 表头操作 | 新增、导入、AI 生成 |165| `[CrudDialogOperation]` | CRUD 弹窗操作 | 编辑、查看详情(简化配置)|166167> 💡 批量操作通过 `[Operation]` 的 `isBatch` 参数实现,设置为 `true` 即可。168169### Operation 特性参数170171```csharp172[Operation(173 label: "解锁", // 按钮文本174 actionType: "ajax", // 操作类型:ajax, form, dialog, aiForm175 dialog: null, // 弹窗配置(可选)176 confirmText: "确定要解除用户锁定吗?", // 确认提示177 visibleOn: "lockoutEnd != null", // 显示条件表达式178 // 多语言支持179 LabelResourceKey = "Operations.Unlock",180 LabelResourceType = typeof(OperationsResources),181 ConfirmTextResourceKey = "Operations.ConfirmUnlock",182 ConfirmTextResourceType = typeof(OperationsResources),183 // 图标184 Icon = "fa-solid fa-unlock")]185```186187### 操作类型说明188189| actionType | 说明 | 适用场景 |190|-----------|------|---------|191| `ajax` | 直接发送请求 | 简单操作(删除、状态切换)|192| `form` | 弹出表单 | 需要输入参数的操作 |193| `dialog` | 弹出对话框 | 复杂内容展示 |194| `aiForm` | AI 长任务表单 | AI 生成、批量处理 |195196### 完整操作示例197198#### 行级 Ajax 操作199200```csharp201[HttpPut("{id}/unlock")]202[Operation("解锁", "ajax", null, "确定要解除用户锁定吗?", "lockoutEnd != null",203 LabelResourceKey = "Operations.Unlock",204 LabelResourceType = typeof(OperationsResources),205 ConfirmTextResourceKey = "Operations.ConfirmUnlock",206 ConfirmTextResourceType = typeof(OperationsResources),207 Icon = "fa-solid fa-unlock")]208[DisplayName("解锁用户")]209public async Task<ActionResult<ApiResponse>> UnlockUser(long id)210{211 await _userService.UnlockUserAsync(id);212 return SuccessResponse("用户已成功解锁。");213}214```215216#### 带反馈结果的操作217218```csharp219[HttpPost("{id}/resetRandomPassword")]220[Operation("重置密码", "ajax", null, "确定要重置密码吗?", "isActive == true",221 LabelResourceKey = "Operations.ResetPassword",222 LabelResourceType = typeof(OperationsResources),223 FeedbackTitle = "重置密码结果",224 FeedbackBodyTpl = @"{225 'type': 'form',226 'body': [227 { 'type': 'button', 'label': '${newPassword}', 'icon': 'fa fa-copy',228 'actionType': 'copy', 'content': '${newPassword}' }229 ]230 }")]231[DisplayName("重置随机密码")]232public async Task<ActionResult<ApiResponse>> ResetRandomPassword(long id)233{234 string newPassword = await _userService.ResetRandomPasswordAsync(id);235 return Ok(new { message = "密码已重置成功!", data = new { newPassword } });236}237```238239#### 带重定向的操作240241```csharp242[HttpPost("{id}/impersonate")]243[Operation("模拟登录", "ajax", null, "确定要模拟此用户登录吗?", "isActive == true",244 LabelResourceKey = "Operations.ImpersonateLogin",245 LabelResourceType = typeof(OperationsResources),246 Redirect = "/impersonate?token=${token}&tenantId=${tenantId}")]247[DisplayName("模拟用户登录")]248public async Task<ActionResult<ApiResponse<object>>> ImpersonateUser(long id) { }249```250251#### AI 长任务操作252253```csharp254[HttpPost("ai/generate")]255[HeaderOperation("AI生成题目", "aiForm",256 Icon = "fa-solid fa-magic",257 StatusApi = "/exam/api/Questions/ai/task-status", // 状态查询 API258 PollingInterval = 2000, // 轮询间隔(毫秒)259 MaxPollingTime = 300000)] // 最大轮询时间260[DisplayName("AI生成题目")]261public async Task<ActionResult<ApiResponse<string>>> GenerateQuestionsAsync(262 [FromBody] GenerateQuestionsRequest request)263{264 var taskId = await _service.GenerateQuestionsAsync(request);265 return SuccessResponse(taskId);266}267```268269#### 批量操作270271```csharp272[HttpPost("batch/delete")]273[Operation("批量删除", "ajax", null, "确定要批量删除?", null, true, // isBatch = true274 LabelResourceKey = "Common.BatchDelete",275 LabelResourceType = typeof(SharedResources),276 ConfirmTextResourceKey = "Common.ConfirmBatchDelete",277 ConfirmTextResourceType = typeof(SharedResources))]278[DisplayName("批量删除用户")]279public async Task<ActionResult<ApiResponse>> BatchDelete([FromBody] BatchOperationDto<long> request)280{281 var (successCount, failedIds) = await _userService.BatchDeleteAsync(request.Ids);282 return SuccessResponse($"成功删除 {successCount} 个用户!");283}284```285286## 构造函数注入287288```csharp289public class UsersController : ApiControllerBase290{291 private readonly IUserService _userService;292 private readonly IAuthService _authService;293 private readonly ILogger<UsersController> _logger;294295 public UsersController(296 IUserService userService,297 IAuthService authService,298 ILogger<UsersController> logger)299 {300 _userService = userService;301 _authService = authService;302 _logger = logger;303 }304}305```306307> 💡 推荐使用主构造函数(C# 12+)简化注入代码。308309## 注意事项3103111. **无需添加 `[ApiController]` 和 `[Route]` 特性**:基类已包含默认配置3122. **路由自动推断**:默认按 `/{service-name}/api/[controller]` 格式3133. **Action 返回 DTO**:不要直接返回实体类3144. **异步方法**:所有 I/O 操作使用 `async/await`3155. **操作特性 Icon**:使用 Font Awesome 图标,格式为 `fa-solid fa-xxx`316317## 参考文件318319- 控制器基类: [ApiControllerBase.cs](mdc:Src/CodeSpirit.Shared/Controllers/ApiControllerBase.cs)320- 导航特性: [NavigationAttribute.cs](mdc:Src/CodeSpirit.Core/Attributes/NavigationAttribute.cs)321- 操作特性: [OperationAttribute.cs](mdc:Src/Components/CodeSpirit.Amis/Attributes/Buttons/OperationAttribute.cs)322- 用户控制器示例: [UsersController.cs](mdc:Src/CodeSpirit.IdentityApi/Controllers/UsersController.cs)323- API 设计规范: [api-design.mdc](mdc:.cursor/rules/api-design.mdc)324
One repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| xin-lai/CodeSpirit.cursor/rules/api-design.mdc · 56 | Cursor rules | no sections | 54/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/dependency-injection.mdc · 56 | Cursor rules | api | 54/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/dto.mdc · 56 | Cursor rules | no sections | 50/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/js.mdc · 56 | Cursor rules | apidocs | 46/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/security.mdc · 56 | Cursor rules | database | 54/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/service.mdc · 56 | Cursor rules | no sections | 50/100 | 14 days ago | |
| xin-lai/CodeSpiritAGENTS.md · 56 | AGENTS.md | styleagent-behaviourdocs | 66/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/ai-development.mdc · 56 | Cursor rules | api | 46/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/amis-cards.mdc · 56 | Cursor rules | no sections | 54/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/csproj.mdc · 56 | Cursor rules | api | 54/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/database.mdc · 56 | Cursor rules | no sections | 74/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/naming-conventions.mdc · 56 | Cursor rules | no sections | 50/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/package-management.mdc · 56 | Cursor rules | no sections | 74/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/project-structure.mdc · 56 | Cursor rules | api | 54/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/testing.mdc · 56 | Cursor rules | testing-strategy | 54/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/cs.mdc · 56 | Cursor rules | no sections | 25/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/all.mdc · 56 | Cursor rules | testing-strategyapi | 50/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/css.mdc · 56 | Cursor rules | ui | 54/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/enum.mdc · 56 | Cursor rules | no sections | 50/100 | 14 days ago | |
| xin-lai/CodeSpirit.cursor/rules/i18n.mdc · 56 | Cursor rules | no sections | 50/100 | 14 days ago |
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| hiromaily/go-crypto-wallet.cursor/rules/typescript.mdc · 126 | Cursor rules | setupbuildtestlint-format+6 | 100/100 | 14 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 46 | Cursor rules | testlint-formatstylearch+5 | 100/100 | 14 days ago | |
| markstev/mark-starter.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+6 | 99/100 | 14 days ago | |
| deifos/clipmira-subtitles.cursor/rules/frontend.mdc · 1 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago | |
| dodgecfr/combatfilms-webapp.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago | |
| Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 14 days ago | |
| langflow-ai/langflow.cursor/rules/docs_development.mdc · 153k | Cursor rules | setupbuildtestlint-format+7 | 97/100 | 14 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-tests.mdc · 46 | Cursor rules | teststyletesting-strategysecurity+3 | 97/100 | 14 days ago |
A badge carrying the measured quality of the strongest agent config file in this repository, out of 100. It reads from this index every time somebody loads your page, so it changes when the measurement changes and there is nothing to keep up to date. Free, no account, and the value is not something you or we can set by hand.
[](https://rulestack.kynth.studio/configs/xin-lai-codespirit-cursor-rules-controller)Would rather not hotlink us? Every badge is also served in shields.io’s endpoint schema, so shields renders the image and your readers never talk to our domain:
Published by Toolproof, the masthead over this index and eight others. The method behind the number is at toolproof.kynth.studio/methodology, and the whole thing is readable as JSON with no key at /api.