بنقرة واحدة
controllers
Use when creating RESTful API controllers with MediatR dispatch and ProblemDetails error responses.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Use when creating RESTful API controllers with MediatR dispatch and ProblemDetails error responses.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
Use when adding caching to .NET APIs or optimizing response times with distributed cache, output cache, or ETags.
Use when configuring API response formats, custom formatters, or Accept header handling.
Use when building controller-based REST APIs with action results, model binding, or MediatR integration.
Use when designing gRPC services, proto files, or adding gRPC-Web or JSON transcoding.
Use when building minimal API endpoints with route groups, filters, or TypedResults.
Use when setting up OpenAPI spec generation or Scalar API documentation UI.
استنادا إلى تصنيف SOC المهني
| name | controllers |
| description | Use when creating RESTful API controllers with MediatR dispatch and ProblemDetails error responses. |
| metadata | {"category":"api","agent":"api-designer","when-to-use":"When creating RESTful API controllers with authorization and ProblemDetails"} |
namespace {Company}.{Domain}.Api.Controllers;
[ApiController]
[Route("api/v1/[controller]")]
[Produces("application/json")]
public abstract class BaseController : ControllerBase
{
private ISender? _mediator;
protected ISender Mediator =>
_mediator ??= HttpContext.RequestServices.GetRequiredService<ISender>();
}
namespace {Company}.{Domain}.Api.Controllers;
[Authorize]
public sealed class OrdersController : BaseController
{
/// <summary>Get paginated orders.</summary>
[HttpGet]
[ProducesResponseType(typeof(PaginatedList<OrderOutput>), 200)]
public async Task<IActionResult> GetAll(
[FromQuery] int page = 1,
[FromQuery] int pageSize = 20,
[FromQuery] string? search = null,
CancellationToken ct = default)
{
var query = new GetOrdersQuery(page, pageSize, search);
var result = await Mediator.Send(query, ct);
return result.IsSuccess
? Ok(result.Value)
: Problem(result.Error);
}
/// <summary>Get order by ID.</summary>
[HttpGet("{id:guid}")]
[ProducesResponseType(typeof(OrderOutput), 200)]
[ProducesResponseType(404)]
public async Task<IActionResult> GetById(Guid id, CancellationToken ct)
{
var result = await Mediator.Send(new GetOrderByIdQuery(id), ct);
return result.IsSuccess
? Ok(result.Value)
: NotFound();
}
/// <summary>Create a new order.</summary>
[HttpPost]
[ProducesResponseType(typeof(Guid), 201)]
[ProducesResponseType(typeof(ValidationProblemDetails), 400)]
public async Task<IActionResult> Create(
[FromBody] CreateOrderCommand command, CancellationToken ct)
{
var result = await Mediator.Send(command, ct);
return result.IsSuccess
? CreatedAtAction(nameof(GetById), new { id = result.Value }, result.Value)
: BadRequest(result.Error);
}
/// <summary>Update an existing order.</summary>
[HttpPut("{id:guid}")]
[ProducesResponseType(204)]
[ProducesResponseType(404)]
public async Task<IActionResult> Update(
Guid id, [FromBody] UpdateOrderCommand command, CancellationToken ct)
{
if (id != command.Id)
return BadRequest("Route ID does not match body ID.");
var result = await Mediator.Send(command, ct);
return result.IsSuccess ? NoContent() : NotFound();
}
/// <summary>Delete an order.</summary>
[HttpDelete("{id:guid}")]
[ProducesResponseType(204)]
[ProducesResponseType(404)]
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
{
var result = await Mediator.Send(new DeleteOrderCommand(id), ct);
return result.IsSuccess ? NoContent() : NotFound();
}
}
namespace {Company}.{Domain}.Api.Middleware;
public sealed class GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger)
: IExceptionHandler
{
public async ValueTask<bool> TryHandleAsync(
HttpContext context, Exception exception, CancellationToken ct)
{
logger.LogError(exception, "Unhandled exception");
var problem = exception switch
{
ValidationException ve => new ProblemDetails
{
Status = 400, Title = "Validation Failed",
Detail = string.Join("; ", ve.Errors.Select(e => e.ErrorMessage))
},
NotFoundException => new ProblemDetails
{
Status = 404, Title = "Not Found"
},
_ => new ProblemDetails
{
Status = 500, Title = "Internal Server Error"
}
};
context.Response.StatusCode = problem.Status!.Value;
await context.Response.WriteAsJsonAsync(problem, ct);
return true;
}
}
// Program.cs
builder.Services.AddControllers();
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseExceptionHandler();
app.MapControllers();
| Anti-Pattern | Correct Approach |
|---|---|
| Business logic in controllers | Delegate to MediatR handlers |
| Returning domain entities directly | Map to output DTOs |
| Manual model validation | Use FluentValidation + pipeline behavior |
| Catching exceptions per action | Use global exception handler |
grep -r "ControllerBase\|ApiController" --include="*.cs"
grep -r "IMediator\|ISender" --include="*.cs"