一键导入
controller-patterns
Use when building controller-based REST APIs with action results, model binding, or MediatR integration.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when building controller-based REST APIs with action results, model binding, or MediatR integration.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
Use when designing gRPC services, proto files, or adding gRPC-Web or JSON transcoding.
Use when writing async code, propagating CancellationTokens, or fixing async/await pitfalls.
Use when applying or enforcing C# coding style — namespaces, sealed classes, var usage, XML docs.
Use when applying modern C# idioms — records, pattern matching, primary constructors, collection expressions.
Use when registering services, choosing lifetimes, or implementing DI patterns like decorator or keyed services.
Use when selecting or implementing design patterns in C# — factory, builder, strategy, decorator, or mediator.
基于 SOC 职业分类
| name | controller-patterns |
| description | Use when building controller-based REST APIs with action results, model binding, or MediatR integration. |
| metadata | {"category":"api","agent":"api-designer"} |
| when_to_use | When creating or modifying controller-based API endpoints with MediatR integration |
[ApiController] for automatic model validation and binding behaviorActionResult<T> with explicit [ProducesResponseType] attributesCancellationToken from every action method[ApiController]
[Route("api/[controller]")]
[Produces("application/json")]
public sealed class OrdersController(ISender sender) : ControllerBase
{
[HttpGet]
[ProducesResponseType(typeof(PagedList<OrderResponse>),
StatusCodes.Status200OK)]
public async Task<ActionResult<PagedList<OrderResponse>>> GetOrders(
[FromQuery] OrderFilter filter, CancellationToken ct)
{
var result = await sender.Send(new ListOrdersQuery(filter), ct);
return Ok(result);
}
[HttpGet("{id:guid}")]
[ProducesResponseType(typeof(OrderResponse), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<OrderResponse>> GetOrder(
Guid id, CancellationToken ct)
{
var result = await sender.Send(new GetOrderQuery(id), ct);
return result is not null ? Ok(result) : NotFound();
}
[HttpPost]
[ProducesResponseType(typeof(OrderResponse),
StatusCodes.Status201Created)]
[ProducesResponseType(typeof(ProblemDetails),
StatusCodes.Status400BadRequest)]
public async Task<ActionResult<OrderResponse>> CreateOrder(
CreateOrderRequest request, CancellationToken ct)
{
var result = await sender.Send(
new CreateOrderCommand(request.CustomerName, request.Items), ct);
return CreatedAtAction(
nameof(GetOrder), new { id = result.Id }, result);
}
[HttpPut("{id:guid}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> UpdateOrder(
Guid id, UpdateOrderRequest request, CancellationToken ct)
{
var result = await sender.Send(
new UpdateOrderCommand(id, request.CustomerName), ct);
return result.Match<IActionResult>(
_ => NoContent(),
error => error.Type == ErrorType.NotFound
? NotFound() : BadRequest(error.ToProblemDetails()));
}
[HttpDelete("{id:guid}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> DeleteOrder(
Guid id, CancellationToken ct)
{
var result = await sender.Send(new DeleteOrderCommand(id), ct);
return result.IsSuccess ? NoContent() : NotFound();
}
}
[ApiController]
[Route("api/orders/{orderId:guid}/items")]
[Produces("application/json")]
public sealed class OrderItemsController(ISender sender) : ControllerBase
{
[HttpGet]
public async Task<ActionResult<List<OrderItemResponse>>> GetItems(
Guid orderId, CancellationToken ct)
{
var result = await sender.Send(
new ListOrderItemsQuery(orderId), ct);
return Ok(result);
}
[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
public async Task<IActionResult> AddItem(
Guid orderId, AddOrderItemRequest request, CancellationToken ct)
{
var result = await sender.Send(
new AddOrderItemCommand(orderId, request.ProductId,
request.Quantity), ct);
return CreatedAtAction(nameof(GetItems), new { orderId }, result);
}
}
// Program.cs
builder.Services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(
new JsonStringEnumConverter());
options.JsonSerializerOptions.DefaultIgnoreCondition =
JsonIgnoreCondition.WhenWritingNull;
});
var app = builder.Build();
app.MapControllers();
builder.Services.AddProblemDetails(options =>
{
options.CustomizeProblemDetails = context =>
{
context.ProblemDetails.Instance =
context.HttpContext.Request.Path;
context.ProblemDetails.Extensions["traceId"] =
Activity.Current?.Id ??
context.HttpContext.TraceIdentifier;
};
});
// FromQuery — query string parameters
public async Task<IActionResult> Search(
[FromQuery] string? name,
[FromQuery] int page = 1) { }
// FromRoute — URL route parameters
[HttpGet("{id:guid}")]
public async Task<IActionResult> Get(
[FromRoute] Guid id) { }
// FromBody — request body (default for complex types with [ApiController])
[HttpPost]
public async Task<IActionResult> Create(
CreateOrderRequest request) { }
// FromHeader — custom headers
public async Task<IActionResult> Process(
[FromHeader(Name = "X-Correlation-Id")] string? correlationId) { }
CancellationToken parameter[ProducesResponseType] attributesISender/IMediator: ControllerBase or : Controller class inheritance[ApiController] attribute on classesControllers/ folderservices.AddControllers() in Program.cs[ProducesResponseType] attributes on actions[ApiController] to all API controllers for automatic validation[ProducesResponseType] to document response types for OpenAPICancellationToken parameter to all async action methodsCreatedAtAction for POST endpoints returning 201| Scenario | Recommendation |
|---|---|
| Complex model binding | Controllers handle this well |
| Need attribute routing | Controllers with [Route] |
| Rapid prototyping | Minimal API may be faster |
| Legacy migration | Keep controllers, modernize patterns |
| OpenAPI generation | Both work, controllers have richer attributes |