用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/Dev-Toolbelt/dev-team-agents --skill single-action-controller命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | single-action-controller |
| description | One controller per HTTP action — naming, structure, DI, routing. |
MANDATORY — Every backend controller must follow this pattern. No exceptions unless the project's existing architecture explicitly pre-dates this rule and migration is out of scope.
One controller class handles exactly one HTTP action. No index, store, show, update, destroy methods in the same class. Each action gets its own file and class.
Why:
| HTTP Action | Class Name | File |
|---|---|---|
GET /orders | ListOrdersController | ListOrdersController.php / list_orders_controller.rb / etc. |
POST /orders | CreateOrderController | CreateOrderController.php |
GET /orders/{id} | ShowOrderController | ShowOrderController.php |
PUT /orders/{id} | UpdateOrderController | UpdateOrderController.php |
DELETE /orders/{id} | DeleteOrderController | DeleteOrderController.php |
POST /orders/{id}/cancel | CancelOrderController | CancelOrderController.php |
Pattern: {Verb}{Resource}Controller — verb is the business action, not the HTTP method.
// PHP (Laravel)
class CreateOrderController
{
public function __construct(
private readonly CreateOrderService $service,
private readonly OrderRequest $request
) {}
public function __invoke(Request $request): JsonResponse
{
$command = CreateOrderCommand::fromRequest($request);
$order = $this->service->execute($command);
return response()->json(OrderResource::make($order), 201);
}
}
// Node.js / NestJS
@Controller('orders')
export class CreateOrderController {
constructor(private readonly service: CreateOrderService) {}
@Post()
async handle(@Body() dto: CreateOrderDto): Promise<OrderResponse> {
return this.service.execute(dto)
}
}
# Python (Django / FastAPI)
class CreateOrderController:
def __init__(self, service: CreateOrderService):
self.service = service
def __call__(self, request: Request) -> Response:
command = CreateOrderCommand.from_request(request)
order = self.service.execute(command)
return Response(OrderSchema.from_orm(order), status=201)
// Go
type CreateOrderHandler struct {
service CreateOrderService
}
func (h *CreateOrderHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
cmd, err := parseCreateOrderCommand(r)
// ...
order, err := h.service.Execute(r.Context(), cmd)
// ...
json.NewEncoder(w).Encode(order)
}
Register each controller separately. Route names must mirror the controller name.
// Laravel
Route::post('/orders', CreateOrderController::class)->name('orders.create');
Route::get('/orders', ListOrdersController::class)->name('orders.list');
Route::get('/orders/{id}', ShowOrderController::class)->name('orders.show');
A controller must never:
app/
Http/
Controllers/
Orders/
CreateOrderController.php
ListOrdersController.php
ShowOrderController.php
UpdateOrderController.php
DeleteOrderController.php
CancelOrderController.php
Users/
CreateUserController.php
...
Group by domain/resource — never by HTTP method.
When reviewing backend code, flag any controller that:
[BLOCKING] Has more than one public action method[BLOCKING] Contains business logic (conditionals on domain state, calculations, DB queries)[BLOCKING] Instantiates its own dependencies (new Service() inside a method)[SUGGESTION] Name does not follow {Verb}{Resource}Controller pattern[SUGGESTION] Controller file is in a flat directory with no domain groupingphp artisan make:controller --resource) — they produce multi-action controllers by default; generate individual files insteadOrderFilter), extract to a Request class or DTO factory — do not merge the controllers