[ApiController]
[Route("api/submissions")]
public class SubmissionsController(OjDbContext db, IJudgeQueue queue) : ControllerBase
{
[HttpPost]
public async Task<ActionResult<SubmissionView>> Submit(SubmitDto dto)
{
var problem = await db.Problems.FindAsync(dto.ProblemId);
if (problem is null) return NotFound();
var submission = new Submission { ProblemId = dto.ProblemId, Language = dto.Language, Status = "PENDING" };
db.Submissions.Add(submission);
await db.SaveChangesAsync();
await queue.EnqueueAsync(submission.Id);
return Created($"/api/submissions/{submission.Id}",
new SubmissionView(submission.Id, "PENDING", submission.CreatedAt));
}
}
// Program.cs:AddControllers() + MapControllers()
步骤 3:校验(DataAnnotations,对应 Bean Validation)
using System.ComponentModel.DataAnnotations;
public record SubmitDto(
[Range(1, long.MaxValue, ErrorMessage = "题目不存在")] long ProblemId,
[Required, RegularExpression("^(cpp|java|python)$")] string Language,
[Required, MaxLength(65536)] string Code);
MapGroup 是 Minimal API 的模块化手段:分组统一加前缀、Tag、鉴权、限流,等价 Controller 类级注解的聚合。
你可能会懵的地方
Minimal API 怎么做「前置校验/日志」这类切面?
Endpoint Filter(AddEndpointFilter),作用于路由命中后的端点,对应 Action Filter / Spring Interceptor:
public class TimingFilter : IEndpointFilter
{
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext ctx, EndpointFilterDelegate next)
{
var sw = Stopwatch.StartNew();
var result = await next(ctx);
Console.WriteLine($"耗时 {sw.ElapsedMilliseconds}ms");
return result;
}
}
CancellationToken 为什么到处出现?
客户端断开/超时通过 HttpContext.RequestAborted 自动注入到参数,传给 EF 异步方法能省掉无效计算——等价 Spring MVC 的请求超时中断,但更显式。
错法对照
错法
问题
正解
接口直接返回 EF 实体
循环引用、字段泄漏、overposting
record DTO + 投影查询
所有接口都返回 200 + 自定义 code 字段
丢掉 HTTP 语义,网关/监控无法识别错误
用状态码 + ProblemDetails
校验写在业务方法里逐个 if
重复、遗漏、不可声明
DataAnnotations / FluentValidation 统一声明
全局异常处理器里返回堆栈给前端
泄漏内部信息
生产环境只返回 title + traceId,日志里留堆栈
卡点急救
POST 报 415:Content-Type 没给 application/json,或客户端发了表单。确认 header 与绑定来源特性。
校验不生效:Controller 忘了 [ApiController];Minimal API 需注册校验过滤器;record 的 required 成员构造失败先于校验,改用 DataAnnotations。
Swagger 页面 404:确认对应环境执行了 UseSwagger/UseSwaggerUI(模板常只在 Development 注册),生产要开就移出条件块并加访问控制。
面试常问
Minimal API 和 Controller 怎么选?底层差异是什么?
底层共用 Endpoint Routing、模型绑定、DI、过滤器管线;差异在门面:Minimal API 用 lambda 注册,无反射扫描,冷启动更优;Controller 提供类级约定与过滤器生态。选型看规模与团队约定,性能差异已不是决定因素。