modules/ai-guide/Write-AiGuide-WebApi.ps1
|
param( [string] $ProjectName, [string] $OutputPath ) . (Join-Path (Split-Path $MyInvocation.MyCommand.Path) "..\core\Utils.ps1") $guideDir = Join-Path $OutputPath "ai-guide" function Write-Guide($fileName, $content) { Write-CoreFile (Join-Path $guideDir $fileName) $content } $webapiGuideContent = @' # WebApi Layer AI Developer Guide You are an AI assistant helping developers build the WebApi Layer of the {P} solution. This layer is the entry point of the API, managing HTTP routing, controller endpoints, validation filters, authorization, and exception handling. --- ## Strict WebApi Rules 1. **Lightweight Controllers:** Controllers must not contain business logic or DB calls. They only accept inputs, invoke MediatR (`_mediator.Send`), and return the result. 2. **ApiResponse Wrapper:** All HTTP actions must return responses wrapped in the `ApiResponse<T>` wrapper (e.g. `return Ok(ApiResponse<CategoryDto>.Ok(result));`). 3. **No Controller Try/Catch:** Do not write try/catch blocks inside controllers. The `GlobalExceptionHandler` automatically intercepts exceptions and returns standardized `ProblemDetails`. 4. **Authorization Policies:** Secure endpoints using role policies like `[Authorize(Policy = "AdminOnly")]` or `[Authorize(Policy = "UserOnly")]`. --- ## How to Add a Controller When adding a controller for a new entity (e.g. `Category`), implement it in `{P}-backend/src/{P}.WebApi/Controllers`. **Required endpoints per entity:** - `GET api/categories` — returns all (internal/admin use) - `GET api/categories/paged` — paginated list for public-facing UI (**always use this for lists**) - `POST api/categories` — create new - `DELETE api/categories/{id}` — soft delete (AdminOnly) ```csharp // {P}-backend/src/{P}.WebApi/Controllers/CategoriesController.cs using MediatR; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using {P}.Application.Common; using {P}.Application.Features.Categories; using {P}.Application.Features.Categories.Commands.AddCategory; using {P}.Application.Features.Categories.Commands.DeleteCategory; using {P}.Application.Features.Categories.Queries.GetAllCategories; using {P}.Application.Features.Categories.Queries.GetPagedCategories; using {P}.Domain.Common; namespace {P}.WebApi.Controllers; [ApiController] [Route("api/[controller]")] [Authorize] public class CategoriesController : ControllerBase { private readonly IMediator _mediator; public CategoriesController(IMediator mediator) => _mediator = mediator; [HttpGet] public async Task<IActionResult> GetAll() => Ok(ApiResponse<object>.Ok(await _mediator.Send(new GetAllCategoriesQuery()))); [HttpGet("paged")] public async Task<IActionResult> GetPaged([FromQuery] int page = 1, [FromQuery] int size = 20) => Ok(ApiResponse<object>.Ok(await _mediator.Send(new GetPagedCategoriesQuery(page, size)))); [HttpPost] public async Task<IActionResult> Add([FromBody] AddCategoryCommand cmd) => Ok(ApiResponse<object>.Ok(new { id = await _mediator.Send(cmd) })); [HttpDelete("{id}")] [Authorize(Policy = "AdminOnly")] public async Task<IActionResult> Delete(int id) => Ok(ApiResponse<object>.Ok(await _mediator.Send(new DeleteCategoryCommand(id)))); } ``` --- ## Global Exception Handling Errors are captured globally by `GlobalExceptionHandler.cs`. The response format automatically maps based on the exception type: - **ValidationException:** Returns HTTP 400 with a dictionary of validation failures. - **KeyNotFoundException:** Returns HTTP 404. - **UnauthorizedAccessException:** Returns HTTP 401. - **Other Exceptions:** Returns HTTP 500. Do not write custom error-wrapping catch blocks; allow the handler to output standardized problem details automatically. '@.Replace("{P}", $ProjectName) Write-Guide "webapi-guide.md" $webapiGuideContent |